From f8f647f012ed3ae929e2c763be3ba240bbed1ce6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 07:01:57 -0500 Subject: [PATCH 001/214] docs first commit --- .../gitLab-configuration-on-narsil.md | 0 .../install-vertexcfd-on-narsil-cpu.md | 0 .../install-vertexcfd-on-narsil-gpu.md | 0 .../sbatch_submission_script | 0 .../temperature-profile-paraview.png | Bin .../sbatch_submission_script | 0 .../trilinos-pardiso-narsil.sh | 0 .../vertexcfd-pardiso-narsil.sh | 0 ...using-epetra-and-pardiso-solvers-in-vertexcfd.md | 0 .../run-vertexcfd/run-incompressible-channel.md | 0 {doc => docs}/run-vertexcfd/run-vertexcfd.md | 0 .../temperature-profile-paraview.png | Bin .../regression-test-suite-for-vertexcfd.md | 0 .../software-quality-assurance.md | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename {doc => docs}/install-vertexcfd/gitLab-configuration-on-narsil.md (100%) rename {doc => docs}/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md (100%) rename {doc => docs}/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md (100%) rename {doc => docs}/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script (100%) rename {doc => docs}/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png (100%) rename {doc => docs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script (100%) rename {doc => docs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh (100%) rename {doc => docs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh (100%) rename {doc => docs}/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md (100%) rename {doc => docs}/run-vertexcfd/run-incompressible-channel.md (100%) rename {doc => docs}/run-vertexcfd/run-vertexcfd.md (100%) rename {doc => docs}/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png (100%) rename {doc => docs}/software-quality-assurance/regression-test-suite-for-vertexcfd.md (100%) rename {doc => docs}/software-quality-assurance/software-quality-assurance.md (100%) diff --git a/doc/install-vertexcfd/gitLab-configuration-on-narsil.md b/docs/install-vertexcfd/gitLab-configuration-on-narsil.md similarity index 100% rename from doc/install-vertexcfd/gitLab-configuration-on-narsil.md rename to docs/install-vertexcfd/gitLab-configuration-on-narsil.md diff --git a/doc/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md b/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md similarity index 100% rename from doc/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md rename to docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md diff --git a/doc/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md b/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md similarity index 100% rename from doc/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md rename to docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md diff --git a/doc/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script b/docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script similarity index 100% rename from doc/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script rename to docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script diff --git a/doc/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png b/docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png similarity index 100% rename from doc/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png rename to docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png diff --git a/doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script b/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script similarity index 100% rename from doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script rename to docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script diff --git a/doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh b/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh similarity index 100% rename from doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh rename to docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh diff --git a/doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh b/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh similarity index 100% rename from doc/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh rename to docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh diff --git a/doc/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md b/docs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md similarity index 100% rename from doc/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md rename to docs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md diff --git a/doc/run-vertexcfd/run-incompressible-channel.md b/docs/run-vertexcfd/run-incompressible-channel.md similarity index 100% rename from doc/run-vertexcfd/run-incompressible-channel.md rename to docs/run-vertexcfd/run-incompressible-channel.md diff --git a/doc/run-vertexcfd/run-vertexcfd.md b/docs/run-vertexcfd/run-vertexcfd.md similarity index 100% rename from doc/run-vertexcfd/run-vertexcfd.md rename to docs/run-vertexcfd/run-vertexcfd.md diff --git a/doc/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png b/docs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png similarity index 100% rename from doc/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png rename to docs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png diff --git a/doc/software-quality-assurance/regression-test-suite-for-vertexcfd.md b/docs/software-quality-assurance/regression-test-suite-for-vertexcfd.md similarity index 100% rename from doc/software-quality-assurance/regression-test-suite-for-vertexcfd.md rename to docs/software-quality-assurance/regression-test-suite-for-vertexcfd.md diff --git a/doc/software-quality-assurance/software-quality-assurance.md b/docs/software-quality-assurance/software-quality-assurance.md similarity index 100% rename from doc/software-quality-assurance/software-quality-assurance.md rename to docs/software-quality-assurance/software-quality-assurance.md From 15b050d3ad475f10d40347f4799da0166d4893cc Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 09:44:25 -0500 Subject: [PATCH 002/214] update READMe file --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6c9c22..1ed1c0c 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ VERTEX-CFD is a performance portable software package for computational fluid dy [![coverage report](https://code-int.ornl.gov/vertex/vertex-cfd/badges/master/coverage.svg)](https://code-int.ornl.gov/mxd/vertex-cfd/-/commits/master?ref_type=heads) -## [CPU BUILD INSTRUCTIONS](doc/install-vertex/install-vertex-on-narsil-cpu.md) +## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) -## [GPU BUILD INSTRUCTIONS](doc/install-vertex/install-vertex-on-narsil-gpu.md) +## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) -## [RUNNING CASES WITH VERTEX-CFD](doc/run-vertex-cfd/Run-incompressible-channel.md) +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/Run-incompressible-channel.md) From 7a38c5940d80884c23257fbf8fbf1099ff2b24c9 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 09:55:42 -0500 Subject: [PATCH 003/214] remove pipeline status --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 1ed1c0c..36201c6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,6 @@ # VERTEX-CFD VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. -## Pipeline status of master branch: -[![pipeline status](https://code-int.ornl.gov/vertex/vertex-cfd/badges/master/pipeline.svg)](https://code-int.ornl.gov/mxd/vertex-cfd/-/commits/master?ref_type=heads) -[![coverage report](https://code-int.ornl.gov/vertex/vertex-cfd/badges/master/coverage.svg)](https://code-int.ornl.gov/mxd/vertex-cfd/-/commits/master?ref_type=heads) - - ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) From 289d9e442fae20c9947223f98e14f3b7e67342a2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 10:10:29 -0500 Subject: [PATCH 004/214] no jekyll build --- .nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .nojekyll diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 From 0532ad68d5a4cb02183081ca2af0a9ed03e82293 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 10:12:31 -0500 Subject: [PATCH 005/214] fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36201c6..ef27273 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,4 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/Run-incompressible-channel.md) +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From 6396366f8a032edd2f6a981789cf2b2da078159f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 10:46:27 -0500 Subject: [PATCH 006/214] adding index file --- docs/index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/index.html diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..e69de29 From a18bd61262f31e523580f8024746bb1289b34ee2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 10:48:28 -0500 Subject: [PATCH 007/214] test index --- docs/index.html | 0 docs/index.md | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 docs/index.html create mode 100644 docs/index.md diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index e69de29..0000000 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..9c4e89a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,2 @@ +# VERTEX-CFD +VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. From 909a1d00649e3ef56961340c35d9ef63e9aa0416 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 26 Nov 2024 11:16:41 -0500 Subject: [PATCH 008/214] update index --- docs/index.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/index.md b/docs/index.md index 9c4e89a..87921c0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,2 +1,9 @@ # VERTEX-CFD VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. + + +## [CPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + +## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + +## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) From 41cece37db4b4909cd273a35a207b621dd418c64 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 2 Dec 2024 15:23:21 -0500 Subject: [PATCH 009/214] add config file --- _config.yml | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..55b7a5d --- /dev/null +++ b/_config.yml @@ -0,0 +1,60 @@ +# Site settings +title: AdditiveFOAM +description: AdditiveFOAM documentation +baseurl: "/AdditiveFOAM" +url: "https://ORNL.github.io" +repository: ORNL/AdditiveFOAM + +permalink: pretty + +remote_theme: just-the-docs/just-the-docs + +plugins: + - jekyll-remote-theme + - jekyll-seo-tag + - jekyll-feed + +compress_html: + blanklines: true + +# Override purple (default) to ORNL green +color_scheme: ornl_green + +logo: "https://raw.githubusercontent.com/ORNL-MDF/additivefoam-website-assets/main/images/logo.png" + +# External navigation links +nav_external_links: + - title: AdditiveFOAM on GitHub + url: "https://github.com/ORNL/AdditiveFOAM" + +# Aux links for the upper right navigation +aux_links: + "AdditiveFOAM on GitHub": + - "https://github.com/ORNL/AdditiveFOAM" + +# Makes Aux links open in a new tab. Default is false +aux_links_new_tab: false + +# Enable callouts in markdown +callouts_level: quiet # or loud +callouts: + highlight: + color: yellow + important: + title: Important + color: blue + new: + title: New + color: green + note: + title: Note + color: purple + warning: + title: Warning + color: red + custom: + title: Note + color: ornl_green + +sass: + custom_stylesheet: _sass/custom/custom.scss From 905d02f302da9440fe809af7b89decd217f03fca Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 2 Dec 2024 15:27:01 -0500 Subject: [PATCH 010/214] remove nojekyll file --- .nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .nojekyll diff --git a/.nojekyll b/.nojekyll deleted file mode 100644 index e69de29..0000000 From cf2c1549010f673ae2f3ba9e9f6db97e1a72db19 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 2 Dec 2024 15:38:00 -0500 Subject: [PATCH 011/214] add index file --- Gemfile.lock | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++ Gemfile.txt | 8 ++ index.md | 49 +++++++++ 3 files changed, 337 insertions(+) create mode 100644 Gemfile.lock create mode 100644 Gemfile.txt create mode 100644 index.md diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..28cd8dd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,280 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.1.3.3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + base64 (0.2.0) + bigdecimal (3.1.8) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + colorator (1.1.0) + commonmarker (0.23.10) + concurrent-ruby (1.3.1) + connection_pool (2.4.1) + dnsruby (1.72.1) + simpleidn (~> 0.2.1) + drb (2.2.1) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.16.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + execjs (2.9.1) + faraday (2.8.1) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) + ffi (1.17.0) + forwardable-extended (2.6.0) + gemoji (4.1.0) + github-pages (231) + github-pages-health-check (= 1.18.2) + jekyll (= 3.9.5) + jekyll-avatar (= 0.8.0) + jekyll-coffeescript (= 1.2.2) + jekyll-commonmark-ghpages (= 0.4.0) + jekyll-default-layout (= 0.1.5) + jekyll-feed (= 0.17.0) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.16.1) + jekyll-include-cache (= 0.2.1) + jekyll-mentions (= 1.6.0) + jekyll-optional-front-matter (= 0.3.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.16.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.3) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.8.0) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) + jekyll-theme-architect (= 0.2.0) + jekyll-theme-cayman (= 0.2.0) + jekyll-theme-dinky (= 0.2.0) + jekyll-theme-hacker (= 0.2.0) + jekyll-theme-leap-day (= 0.2.0) + jekyll-theme-merlot (= 0.2.0) + jekyll-theme-midnight (= 0.2.0) + jekyll-theme-minimal (= 0.2.0) + jekyll-theme-modernist (= 0.2.0) + jekyll-theme-primer (= 0.6.0) + jekyll-theme-slate (= 0.2.0) + jekyll-theme-tactile (= 0.2.0) + jekyll-theme-time-machine (= 0.2.0) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.13.0) + kramdown (= 2.4.0) + kramdown-parser-gfm (= 1.1.0) + liquid (= 4.0.4) + mercenary (~> 0.3) + minima (= 2.5.1) + nokogiri (>= 1.13.6, < 2.0) + rouge (= 3.30.0) + terminal-table (~> 1.4) + github-pages-health-check (1.18.2) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (>= 4, < 8) + public_suffix (>= 3.0, < 6.0) + typhoeus (~> 1.3) + html-pipeline (2.14.3) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.8.0) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + jekyll (3.9.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (>= 0.7, < 2) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (>= 1.17, < 3) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.8.0) + jekyll (>= 3.0, < 5.0) + jekyll-coffeescript (1.2.2) + coffee-script (~> 2.2) + coffee-script-source (~> 1.12) + jekyll-commonmark (1.4.0) + commonmarker (~> 0.22) + jekyll-commonmark-ghpages (0.4.0) + commonmarker (~> 0.23.7) + jekyll (~> 3.9.0) + jekyll-commonmark (~> 1.4.0) + rouge (>= 2.0, < 5.0) + jekyll-default-layout (0.1.5) + jekyll (>= 3.0, < 5.0) + jekyll-feed (0.17.0) + jekyll (>= 3.7, < 5.0) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.16.1) + jekyll (>= 3.4, < 5.0) + octokit (>= 4, < 7, != 4.4.0) + jekyll-include-cache (0.2.1) + jekyll (>= 3.7, < 5.0) + jekyll-mentions (1.6.0) + html-pipeline (~> 2.3) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-spaceship (0.5.3) + jekyll (>= 3.6, < 5.0) + nokogiri (~> 1.6) + rainbow (~> 3.0) + jekyll-swiss (1.0.0) + jekyll-theme-architect (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.6.0) + jekyll (> 3.5, < 5.0) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.13.0) + gemoji (>= 3, < 5) + html-pipeline (~> 2.2) + jekyll (>= 3.0, < 5.0) + just-the-docs (0.8.2) + jekyll (>= 3.8.5) + jekyll-include-cache + jekyll-seo-tag (>= 2.0) + rake (>= 12.3.1) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.23.1) + mutex_m (0.2.0) + nokogiri (1.15.6-x86_64-linux) + racc (~> 1.4) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (5.0.5) + racc (1.8.0) + rainbow (3.1.1) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rexml (3.2.8) + strscan (>= 3.0.9) + rouge (3.30.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + simpleidn (0.2.3) + strscan (3.1.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (1.8.0) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + github-pages + jekyll-spaceship + just-the-docs + +BUNDLED WITH + 2.4.22 diff --git a/Gemfile.txt b/Gemfile.txt new file mode 100644 index 0000000..b41a7b4 --- /dev/null +++ b/Gemfile.txt @@ -0,0 +1,8 @@ +source 'https://rubygems.org' +gem 'github-pages', group: :jekyll_plugins + +# Add just-the-docs theme +gem 'just-the-docs' + +# Add spaceship to have mathjax +gem "jekyll-spaceship" diff --git a/index.md b/index.md new file mode 100644 index 0000000..a82feac --- /dev/null +++ b/index.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Home +nav_order: 1 +permalink: / +--- + +image + +An open-source CFD code for additive manufacturing built on OpenFOAM. +{: .fs-6 .fw-300 } + +[User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } +[View it on GitHub][AdditiveFOAM repo]{: .btn .fs-5 .mb-4 .mb-md-0 } + +--- + +## Contributing +We encourage you to contribute to AdditiveFOAM! Please check the [guidelines](https://github.com/ORNL/AdditiveFOAM/blob/main/CONTRIBUTING.md) on how to do so. + +#### Contributors +- [John Coleman](https://www.ornl.gov/staff-profile/john-s-coleman) +- [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) +- [Gerry L. Knapp](https://www.ornl.gov/staff-profile/gerald-l-knapp) +- [Benjamin Stump](https://www.ornl.gov/staff-profile/benjamin-c-stump) +- [Alex Plotkowski](https://www.ornl.gov/staff-profile/alex-j-plotkowski) +- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) + + +## Citing +If you use AdditiveFoam in your work, please cite the Zenodo DOI [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8034098.svg)](https://doi.org/10.5281/zenodo.8034098) of the version you used as a software citation: +```bibtex +@software{AdditiveFOAM_1.0.0, + author = {John Coleman and + Kellis Kincaid and + Gerald L. Knapp and + Benjamin Stump and + Alexander J. Plotkowski}, + title = {AdditiveFOAM: Release 1.0}, + month = jun, + year = 2023, + publisher = {Zenodo}, + version = {1.0.0}, + doi = {10.5281/zenodo.8034098}, + url = {https://doi.org/10.5281/zenodo.8034098} +} +``` + +[AdditiveFOAM repo]: https://github.com/ORNL/AdditiveFOAM From 82665d3511d33f5d099770361320aaa1ff17cd78 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 08:39:13 -0500 Subject: [PATCH 012/214] rename readme file --- README.md => save_file | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.md => save_file (100%) diff --git a/README.md b/save_file similarity index 100% rename from README.md rename to save_file From 3f3a702cdec316036a6dd9e1599a24d7d98c7114 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 08:48:15 -0500 Subject: [PATCH 013/214] minor change --- index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/index.md b/index.md index a82feac..e6d1491 100644 --- a/index.md +++ b/index.md @@ -25,6 +25,7 @@ We encourage you to contribute to AdditiveFOAM! Please check the [guidelines](ht - [Benjamin Stump](https://www.ornl.gov/staff-profile/benjamin-c-stump) - [Alex Plotkowski](https://www.ornl.gov/staff-profile/alex-j-plotkowski) - [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) +- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) ## Citing From 38240c2b28b7f13afb5c1ff5fb8ee27b77f1f9b5 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 09:00:36 -0500 Subject: [PATCH 014/214] update website --- _config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_config.yml b/_config.yml index 55b7a5d..a1fc77c 100644 --- a/_config.yml +++ b/_config.yml @@ -1,9 +1,9 @@ # Site settings title: AdditiveFOAM description: AdditiveFOAM documentation -baseurl: "/AdditiveFOAM" +baseurl: "/VERTEX-CFD" url: "https://ORNL.github.io" -repository: ORNL/AdditiveFOAM +repository: ORNL/VERTEX-CFD permalink: pretty @@ -24,13 +24,13 @@ logo: "https://raw.githubusercontent.com/ORNL-MDF/additivefoam-website-assets/ma # External navigation links nav_external_links: - - title: AdditiveFOAM on GitHub - url: "https://github.com/ORNL/AdditiveFOAM" + - title: VERTEX-CFD on GitHub + url: "https://github.com/ORNL/VERTEX-CFD" # Aux links for the upper right navigation aux_links: "AdditiveFOAM on GitHub": - - "https://github.com/ORNL/AdditiveFOAM" + - "https://github.com/ORNL/VERTEX-CFD" # Makes Aux links open in a new tab. Default is false aux_links_new_tab: false From f998aa8a11ef2c5af40658b0b57b22f48cc4a717 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 09:03:20 -0500 Subject: [PATCH 015/214] minor update --- _config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_config.yml b/_config.yml index a1fc77c..608a7bd 100644 --- a/_config.yml +++ b/_config.yml @@ -1,6 +1,6 @@ # Site settings -title: AdditiveFOAM -description: AdditiveFOAM documentation +title: VERTEX-CFD +description: VERTEX-CFD documentation baseurl: "/VERTEX-CFD" url: "https://ORNL.github.io" repository: ORNL/VERTEX-CFD @@ -29,7 +29,7 @@ nav_external_links: # Aux links for the upper right navigation aux_links: - "AdditiveFOAM on GitHub": + "VERTEX-CFD on GitHub": - "https://github.com/ORNL/VERTEX-CFD" # Makes Aux links open in a new tab. Default is false From 068521c64ff486924ee629c66c1b0efeeca8fc60 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:23:57 -0500 Subject: [PATCH 016/214] update --- Gemfile.txt => Gemfile | 0 _config.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Gemfile.txt => Gemfile (100%) diff --git a/Gemfile.txt b/Gemfile similarity index 100% rename from Gemfile.txt rename to Gemfile diff --git a/_config.yml b/_config.yml index 608a7bd..8fe81ef 100644 --- a/_config.yml +++ b/_config.yml @@ -7,7 +7,7 @@ repository: ORNL/VERTEX-CFD permalink: pretty -remote_theme: just-the-docs/just-the-docs +theme: just-the-docs plugins: - jekyll-remote-theme From 9e8847484dca55201fc544df15ba42c0aefbf873 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:41:35 -0500 Subject: [PATCH 017/214] update --- Gemfile | 8 -- Gemfile.lock | 280 --------------------------------------------------- _config.yml | 60 ----------- index.md | 52 ++-------- save_file | 8 -- 5 files changed, 6 insertions(+), 402 deletions(-) delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 _config.yml delete mode 100644 save_file diff --git a/Gemfile b/Gemfile deleted file mode 100644 index b41a7b4..0000000 --- a/Gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source 'https://rubygems.org' -gem 'github-pages', group: :jekyll_plugins - -# Add just-the-docs theme -gem 'just-the-docs' - -# Add spaceship to have mathjax -gem "jekyll-spaceship" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 28cd8dd..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,280 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (7.1.3.3) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) - base64 (0.2.0) - bigdecimal (3.1.8) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - colorator (1.1.0) - commonmarker (0.23.10) - concurrent-ruby (1.3.1) - connection_pool (2.4.1) - dnsruby (1.72.1) - simpleidn (~> 0.2.1) - drb (2.2.1) - em-websocket (0.5.3) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0) - ethon (0.16.0) - ffi (>= 1.15.0) - eventmachine (1.2.7) - execjs (2.9.1) - faraday (2.8.1) - base64 - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) - faraday-net_http (3.0.2) - ffi (1.17.0) - forwardable-extended (2.6.0) - gemoji (4.1.0) - github-pages (231) - github-pages-health-check (= 1.18.2) - jekyll (= 3.9.5) - jekyll-avatar (= 0.8.0) - jekyll-coffeescript (= 1.2.2) - jekyll-commonmark-ghpages (= 0.4.0) - jekyll-default-layout (= 0.1.5) - jekyll-feed (= 0.17.0) - jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.16.1) - jekyll-include-cache (= 0.2.1) - jekyll-mentions (= 1.6.0) - jekyll-optional-front-matter (= 0.3.2) - jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.16.0) - jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.3) - jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.8.0) - jekyll-sitemap (= 1.4.0) - jekyll-swiss (= 1.0.0) - jekyll-theme-architect (= 0.2.0) - jekyll-theme-cayman (= 0.2.0) - jekyll-theme-dinky (= 0.2.0) - jekyll-theme-hacker (= 0.2.0) - jekyll-theme-leap-day (= 0.2.0) - jekyll-theme-merlot (= 0.2.0) - jekyll-theme-midnight (= 0.2.0) - jekyll-theme-minimal (= 0.2.0) - jekyll-theme-modernist (= 0.2.0) - jekyll-theme-primer (= 0.6.0) - jekyll-theme-slate (= 0.2.0) - jekyll-theme-tactile (= 0.2.0) - jekyll-theme-time-machine (= 0.2.0) - jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.13.0) - kramdown (= 2.4.0) - kramdown-parser-gfm (= 1.1.0) - liquid (= 4.0.4) - mercenary (~> 0.3) - minima (= 2.5.1) - nokogiri (>= 1.13.6, < 2.0) - rouge (= 3.30.0) - terminal-table (~> 1.4) - github-pages-health-check (1.18.2) - addressable (~> 2.3) - dnsruby (~> 1.60) - octokit (>= 4, < 8) - public_suffix (>= 3.0, < 6.0) - typhoeus (~> 1.3) - html-pipeline (2.14.3) - activesupport (>= 2) - nokogiri (>= 1.4) - http_parser.rb (0.8.0) - i18n (1.14.5) - concurrent-ruby (~> 1.0) - jekyll (3.9.5) - addressable (~> 2.4) - colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (>= 0.7, < 2) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (>= 1.17, < 3) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 4) - safe_yaml (~> 1.0) - jekyll-avatar (0.8.0) - jekyll (>= 3.0, < 5.0) - jekyll-coffeescript (1.2.2) - coffee-script (~> 2.2) - coffee-script-source (~> 1.12) - jekyll-commonmark (1.4.0) - commonmarker (~> 0.22) - jekyll-commonmark-ghpages (0.4.0) - commonmarker (~> 0.23.7) - jekyll (~> 3.9.0) - jekyll-commonmark (~> 1.4.0) - rouge (>= 2.0, < 5.0) - jekyll-default-layout (0.1.5) - jekyll (>= 3.0, < 5.0) - jekyll-feed (0.17.0) - jekyll (>= 3.7, < 5.0) - jekyll-gist (1.5.0) - octokit (~> 4.2) - jekyll-github-metadata (2.16.1) - jekyll (>= 3.4, < 5.0) - octokit (>= 4, < 7, != 4.4.0) - jekyll-include-cache (0.2.1) - jekyll (>= 3.7, < 5.0) - jekyll-mentions (1.6.0) - html-pipeline (~> 2.3) - jekyll (>= 3.7, < 5.0) - jekyll-optional-front-matter (0.3.2) - jekyll (>= 3.0, < 5.0) - jekyll-paginate (1.1.0) - jekyll-readme-index (0.3.0) - jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.16.0) - jekyll (>= 3.3, < 5.0) - jekyll-relative-links (0.6.1) - jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.3) - addressable (~> 2.0) - jekyll (>= 3.5, < 5.0) - jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) - rubyzip (>= 1.3.0, < 3.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.8.0) - jekyll (>= 3.8, < 5.0) - jekyll-sitemap (1.4.0) - jekyll (>= 3.7, < 5.0) - jekyll-spaceship (0.5.3) - jekyll (>= 3.6, < 5.0) - nokogiri (~> 1.6) - rainbow (~> 3.0) - jekyll-swiss (1.0.0) - jekyll-theme-architect (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-cayman (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-dinky (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-leap-day (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-merlot (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-midnight (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-minimal (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-modernist (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.6.0) - jekyll (> 3.5, < 5.0) - jekyll-github-metadata (~> 2.9) - jekyll-seo-tag (~> 2.0) - jekyll-theme-slate (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-tactile (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-theme-time-machine (0.2.0) - jekyll (> 3.5, < 5.0) - jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.3) - jekyll (>= 3.3, < 5.0) - jekyll-watch (2.2.1) - listen (~> 3.0) - jemoji (0.13.0) - gemoji (>= 3, < 5) - html-pipeline (~> 2.2) - jekyll (>= 3.0, < 5.0) - just-the-docs (0.8.2) - jekyll (>= 3.8.5) - jekyll-include-cache - jekyll-seo-tag (>= 2.0) - rake (>= 12.3.1) - kramdown (2.4.0) - rexml - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - liquid (4.0.4) - listen (3.9.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - mercenary (0.3.6) - minima (2.5.1) - jekyll (>= 3.5, < 5.0) - jekyll-feed (~> 0.9) - jekyll-seo-tag (~> 2.1) - minitest (5.23.1) - mutex_m (0.2.0) - nokogiri (1.15.6-x86_64-linux) - racc (~> 1.4) - octokit (4.25.1) - faraday (>= 1, < 3) - sawyer (~> 0.9) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (5.0.5) - racc (1.8.0) - rainbow (3.1.1) - rake (13.2.1) - rb-fsevent (0.11.2) - rb-inotify (0.11.1) - ffi (~> 1.0) - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (3.30.0) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - safe_yaml (1.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.9.2) - addressable (>= 2.3.5) - faraday (>= 0.17.3, < 3) - simpleidn (0.2.3) - strscan (3.1.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - unicode-display_width (1.8.0) - -PLATFORMS - x86_64-linux - -DEPENDENCIES - github-pages - jekyll-spaceship - just-the-docs - -BUNDLED WITH - 2.4.22 diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 8fe81ef..0000000 --- a/_config.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Site settings -title: VERTEX-CFD -description: VERTEX-CFD documentation -baseurl: "/VERTEX-CFD" -url: "https://ORNL.github.io" -repository: ORNL/VERTEX-CFD - -permalink: pretty - -theme: just-the-docs - -plugins: - - jekyll-remote-theme - - jekyll-seo-tag - - jekyll-feed - -compress_html: - blanklines: true - -# Override purple (default) to ORNL green -color_scheme: ornl_green - -logo: "https://raw.githubusercontent.com/ORNL-MDF/additivefoam-website-assets/main/images/logo.png" - -# External navigation links -nav_external_links: - - title: VERTEX-CFD on GitHub - url: "https://github.com/ORNL/VERTEX-CFD" - -# Aux links for the upper right navigation -aux_links: - "VERTEX-CFD on GitHub": - - "https://github.com/ORNL/VERTEX-CFD" - -# Makes Aux links open in a new tab. Default is false -aux_links_new_tab: false - -# Enable callouts in markdown -callouts_level: quiet # or loud -callouts: - highlight: - color: yellow - important: - title: Important - color: blue - new: - title: New - color: green - note: - title: Note - color: purple - warning: - title: Warning - color: red - custom: - title: Note - color: ornl_green - -sass: - custom_stylesheet: _sass/custom/custom.scss diff --git a/index.md b/index.md index e6d1491..b4cb4f2 100644 --- a/index.md +++ b/index.md @@ -1,50 +1,10 @@ ---- -layout: default -title: Home -nav_order: 1 -permalink: / ---- +# VERTEX-CFD +VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. -image +## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) -An open-source CFD code for additive manufacturing built on OpenFOAM. -{: .fs-6 .fw-300 } +## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) -[User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } -[View it on GitHub][AdditiveFOAM repo]{: .btn .fs-5 .mb-4 .mb-md-0 } +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) ---- - -## Contributing -We encourage you to contribute to AdditiveFOAM! Please check the [guidelines](https://github.com/ORNL/AdditiveFOAM/blob/main/CONTRIBUTING.md) on how to do so. - -#### Contributors -- [John Coleman](https://www.ornl.gov/staff-profile/john-s-coleman) -- [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) -- [Gerry L. Knapp](https://www.ornl.gov/staff-profile/gerald-l-knapp) -- [Benjamin Stump](https://www.ornl.gov/staff-profile/benjamin-c-stump) -- [Alex Plotkowski](https://www.ornl.gov/staff-profile/alex-j-plotkowski) -- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) -- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) - - -## Citing -If you use AdditiveFoam in your work, please cite the Zenodo DOI [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8034098.svg)](https://doi.org/10.5281/zenodo.8034098) of the version you used as a software citation: -```bibtex -@software{AdditiveFOAM_1.0.0, - author = {John Coleman and - Kellis Kincaid and - Gerald L. Knapp and - Benjamin Stump and - Alexander J. Plotkowski}, - title = {AdditiveFOAM: Release 1.0}, - month = jun, - year = 2023, - publisher = {Zenodo}, - version = {1.0.0}, - doi = {10.5281/zenodo.8034098}, - url = {https://doi.org/10.5281/zenodo.8034098} -} -``` - -[AdditiveFOAM repo]: https://github.com/ORNL/AdditiveFOAM +test diff --git a/save_file b/save_file deleted file mode 100644 index ef27273..0000000 --- a/save_file +++ /dev/null @@ -1,8 +0,0 @@ -# VERTEX-CFD -VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. - -## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - -## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From ded17df224e3e7cecc379527d7756724c713b55e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:45:11 -0500 Subject: [PATCH 018/214] update --- index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index b4cb4f2..576ab3c 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -# VERTEX-CFD +# VERTEX-CFD README file VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) @@ -7,4 +7,4 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) -test +test3 From c8a39a6f342312b1dc27e088c435a6a9de17fea1 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:47:10 -0500 Subject: [PATCH 019/214] no jekyll --- .nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .nojekyll diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 From 2bc1ae3ec58d890772cc9731fc1c5569fa23fc1d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:51:59 -0500 Subject: [PATCH 020/214] update --- docs/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.md b/docs/index.md index 87921c0..578b042 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,3 +7,5 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) ## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) + +test4 From 1c3e76129a3e4d4192d2891d41fffc581d5135a1 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 10:58:18 -0500 Subject: [PATCH 021/214] update --- .nojekyll => docs/.nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .nojekyll => docs/.nojekyll (100%) diff --git a/.nojekyll b/docs/.nojekyll similarity index 100% rename from .nojekyll rename to docs/.nojekyll From a3119359e81ba3cd085fd63f54f4ce3fe1f2cf2f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:00:43 -0500 Subject: [PATCH 022/214] update --- docs/index.md | 3 +-- index.md | 10 ---------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 index.md diff --git a/docs/index.md b/docs/index.md index 578b042..86f19e4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,6 @@ -# VERTEX-CFD +# VERTEX-CFD inside docs VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. - ## [CPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) ## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) diff --git a/index.md b/index.md deleted file mode 100644 index 576ab3c..0000000 --- a/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# VERTEX-CFD README file -VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. - -## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - -## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) - -test3 From 7cf46338941bb202a23e38061e3824ae0fccb743 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:02:44 -0500 Subject: [PATCH 023/214] update --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 86f19e4..f257827 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,3 +8,4 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) test4 +test5 From 533b99ad9451efdca32a6fdce6d4fa6835020ca6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:13:13 -0500 Subject: [PATCH 024/214] update --- index.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 index.md diff --git a/index.md b/index.md new file mode 100644 index 0000000..df6b0d2 --- /dev/null +++ b/index.md @@ -0,0 +1 @@ +test3 From 3713b5662401ad132b3de24163f4a5949ce8cfb3 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:28:54 -0500 Subject: [PATCH 025/214] update again --- docs/index.md | 1 - index.md | 1 - 2 files changed, 2 deletions(-) delete mode 100644 index.md diff --git a/docs/index.md b/docs/index.md index f257827..3fbf1f1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,5 +7,4 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) -test4 test5 diff --git a/index.md b/index.md deleted file mode 100644 index df6b0d2..0000000 --- a/index.md +++ /dev/null @@ -1 +0,0 @@ -test3 From 2e3e8ab56f10bc81bdd98dc70c0461b4e4d06bb2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:34:57 -0500 Subject: [PATCH 026/214] update again 2 --- index.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 index.md diff --git a/index.md b/index.md new file mode 100644 index 0000000..80f0a52 --- /dev/null +++ b/index.md @@ -0,0 +1,10 @@ +# VERTEX-CFD root +VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. + +## [CPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + +## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + +## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) + +test6 From 2bc4b403c706252b258ab1efec536b7ec9356093 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:38:01 -0500 Subject: [PATCH 027/214] update review 3 --- index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index 80f0a52..49e0341 100644 --- a/index.md +++ b/index.md @@ -1,10 +1,10 @@ # VERTEX-CFD root VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. -## [CPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) +## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) -## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) +## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) -## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) test6 From 2d0f8809353c921484d8d6dd5de675939e67d530 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:40:32 -0500 Subject: [PATCH 028/214] update again 4 --- docs/index.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 docs/index.md diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 3fbf1f1..0000000 --- a/docs/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# VERTEX-CFD inside docs -VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. - -## [CPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - -## [GPU BUILD INSTRUCTIONS](install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - -## [RUNNING CASES WITH VERTEX-CFD](run-vertexcfd/run-incompressible-channel.md) - -test5 From 295a53416b5eb8f29c18a08d87835b6c5b8baf19 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:45:09 -0500 Subject: [PATCH 029/214] update again 6 --- index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/index.md b/index.md index 49e0341..2459962 100644 --- a/index.md +++ b/index.md @@ -8,3 +8,4 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) test6 +test7 From e76653f0f671ecbf6d029a0dcd0f03bcdf073050 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:46:54 -0500 Subject: [PATCH 030/214] remove nojekyll file --- docs/.nojekyll | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/.nojekyll diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 From 27c285877fe6ad415e3d3fc324790edb51adda5d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 11:55:40 -0500 Subject: [PATCH 031/214] add config gile --- _config.yml | 6 ++++++ index.md | 1 + 2 files changed, 7 insertions(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c90d804 --- /dev/null +++ b/_config.yml @@ -0,0 +1,6 @@ +title: Minimal theme +logo: /assets/img/logo.png +description: Minimal is a theme for GitHub Pages. +show_downloads: true +google_analytics: +theme: jekyll-theme-minimal diff --git a/index.md b/index.md index 2459962..f8eb59e 100644 --- a/index.md +++ b/index.md @@ -8,4 +8,5 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) test6 + test7 From 61495af97e84415ce5a3098c5e64b930f54f257d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 12:03:22 -0500 Subject: [PATCH 032/214] update 11 --- _config.yml | 2 +- docs/figures/logo.png | Bin 0 -> 297233 bytes index.md | 6 +----- 3 files changed, 2 insertions(+), 6 deletions(-) create mode 100644 docs/figures/logo.png diff --git a/_config.yml b/_config.yml index c90d804..a075c04 100644 --- a/_config.yml +++ b/_config.yml @@ -1,5 +1,5 @@ title: Minimal theme -logo: /assets/img/logo.png +logo: docs/figures/logo.png description: Minimal is a theme for GitHub Pages. show_downloads: true google_analytics: diff --git a/docs/figures/logo.png b/docs/figures/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..33f5012dbcf060ff7f7b6873474a2b4af4c353fe GIT binary patch literal 297233 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�DM? zvpfc2D8zbWag3~h6gsM)yO7X5ZWEnM!a$Gl4b0P5X~dLNV%d_SD5h3p_2&qxtBv;u zzGUoeB4!=EZq2r>u6iDe4%E zZzx6<`kz9`_7O59)5TIQF|`6E7bI$JdMR0PsYEhc42tO|$`KxQ)b7mwp1fK1Xgu#8_Ns3lx?u}k$x-;b_s=+rAkR!Q33t;t+4!6nNO z)YE897lQm;wXbDf(Z0jse1)WlltsRw^)BkW4yo#XcLOWUo_t1Fh*<~0-ok}sY{=Js ziF$=`FX8Keji3FRgOp$cHE>&|TXW^NE~cDH1<+&i9P@h>F~QD6Tnr>{Xw*-?^0{2F zF*}5oDffS)+X6-c=R&-WMEP`JTZi}*8k2X;u|9KQ`-_hM&0 zWjD^RCaNm>#>hA2$e~_?BjmC4AL7Z5kC+6HtJ?D-@n_)@4{pXB6oONB)Zrh5iaB$r zb5g6xXcj^Ua=$;|ez0ME5SHCQ{qiGxT!bHgx(4}*G7u$MTN^!DzEQM{>}8-nfgsU+ z|5xhnA3=oJc&SRy_R9ItwnRc^30Y5z2Z2d_2G9?%x$$55*#ihRe_CFqgbkPulmrW} zUk98y8D9up7)i(BCb3qV7WPTx;gLHZ8bI6LFsyh)@~XN=ZImfR6t=hZL6k0M>ywgimU6KI&<5JQ zAFrv>fjZR{5$x;u)cly8%8!`XvvEAIXjO9er%|n+4IPIoi5fY_(uA$D4aP0WDbI>T zusnpsyDjYh{b)TQnhwI|piS@R2Y?Y{y_s^LU*Z{5k>%eCfYL-B`Wz2P(a0gqN`PEQ zHzm9cXIXR(W$aQ>TMz-aiPZRK>ld-kw+p;1G))PfsSgQysV-?=Ds@Z_qbQ3~l#HJQ zov7ZFJNny%50s`(UiX<3=n1SIlZ11h5~|s4GpKphhCZfqT~Gb3{HIUCmrmkUq|k@h z{S43IX}ZzQTi8dnaphu1IF%@=*cpGq_~CFf91NLB#o+#P}TTBDyF z9aW`~SU z64f*a3%54+$%$>Y1^op9NZCA+(^~k0Dl;=P$`&}bfm2V_AX8shF_lZ)tWuzOvbWFb z6q2Hcxi4c<0_mW|JqqElq48$^b?Kgj|4w)iR5Cu-$~krUs!+?!JsMJ(TaVD2c~UXa z6w>`RSkX`2DB5<{;|w~zJV9mg#I%f+qDf*(CTh}F5G64b`(btafVVYx`q%R}k7b!e zyaOGGCn|-DXCW~39K_GmSkFtd{`~nR+2w&8HHmN8??Hd5!q&qerQEZ$jFaj#pfn&n zhb!L=JZ{uthQ(M`0OfX`3V4>YTz?6u_uK+zc_(#NxGTd#jMlxsBC3OKXha@5xjO5$ zhwZU4*xofoIAy$5oegh#ZuFEaqCD$oVEkE6zkb$y_%~+9o2IR@DAjj#0Se{R(9yZ~ z`jCc8)=xOe4r7p3dCL<~)H8J@4zb?+Mp!ytS=IJ1*tgt_=2uDC4c<>eD6}O}*Fwdn zkmdtHFmpeJR~J%~I_m5LrGm?ce^M4>k^b(*6sZZa*Xfg3 z?iSA_N(8hl2URY$E_6yw@)qwD@#ZeChBqH1$6dw?cN|esiDUEyI%4C4q?QI1jXNcG z>Nbdg9a;+X8TqSLR%bj$kqLb&hf#3>2YdUGyc{ad7)D$V3PRLwACk%RLqvDG74TK_ zLOIWUq|Qb4c36I&KDD+}`IDRo>oUtO8)dRk-5LSbRdR$Khe>8mC(D0Qlv+ZN$;r2a}X`Nrtq-uJfB9W0Gh&k8U4l}r|_>YnNoifEzidF6`^R>gVL zCThGodV5_q$P_|c+lLrZ0mEW`CkaDKN-|ZccgGvDVjm}s95zoo*oy1vmiGr6f6K?ckg8M%LrB=N^?bm)=iy=Y`Vc>2jP7arYR9K$s8fg73Qr9&R*KJ{^BdpG zeDcZ=?bXkD5khrHg590L&AWrVKSfrk-2d+dxYQ@GC_lt347~p}U!_)pk3;s>-$p@S zr%dlCCXilh==8KeLW_D1hq_Cm9Mz9Ie_qu8Kmp}BNCvPY>3(HQbejg)mKqF4%OWe- zoZp*h9k4*>5;y21lvL^|yHPzBJ+PuiRdf ze0q|?0Z+M~QBN+@=Zt>lk{PUJQQ$%*Se{;6ILF^(F=0Rrm4Jm9@Rl*inFMYcNV*P1 z2y|$BsM^}w!sPFx7X8Itaf*lMTbMfi3Vdf*uM+F3e0=>wiw{Y+8u0stkCCK^EM{hi z(v`Liz&-V=8pYCniliNhg(Cp1%_FWQs72XcSh$q$Z$3nzTK;&WW(*!fT5CE>P)NAS zA22OL6I;A)zStm%$47q>M|wXByjsh;D&!V#yItIo_7WsBk1nQ`xIq;*TIz;sQ@azT ziDs&uI|%)BuJR5%UTP{yztaDuFNzrM1^5pQ6;sLN)fDyonK)=r-L=MhIdNHuDgMS0 z)v_=Zq1ig;W$*V^|7CI;rYXT+auUY-Z=EzOZ(iVBNP7t+ z06^XT1B+Y0%=6uo<_c3;fOl5bV-dfy@>0Qc=s_qndj?%m^$8K_`w~RXC*crB2v5 z_k#Ox3l*^d{Eq3FK;;xsBY%qwb{5_dm0cM|C5Lt zGVn^*i?V#~Qmk6RT*b76f>!^az(Lcdp~vtjB#V{WWD`8~W1l~#AUW*&?JMfvXORa` zwLI#t=9fQ_t&R~6y1nYK7QfWgGfuG)Z2jm{hOSZHuo!b5#s(q!HOVEH=fb^a7UuSs z=x(nGpMYjqVc~2Z-H5Z~qtx>7wYIHwMSSUi?!DT=lBY|MMh%rb@7jMGLh6Z9x%;O{ z3e6)DP18$(qx$pGfK+%{)5WfLFDlc{V$UCfiA+=!xgB8idJNKYZ_4-G5kr#V_Pv6> zi)W5jwtAGlufpil>{x*94N9doXel2QA~?^6Kr!pu+;}?j2tOG7nEZ906jnP<$?opS z9!tl&_G`rJRk{tU`(3s^q}j&CnrE@vXHoKlg!#3)z-c!3imzk7FZrc8j8yW9LZbhG zX~8i#CP6tpPe{m}nZ(jeY*4JDTE{ut1nKXGRyrsVk(2$({GFBAwH{CXrEdP!t-dvl zq1({sWgFDskvQ*wHeA&H`1`bxGy2$d;c>iakvG9u?)G|vQ4VSx|61Zzz&RbSu-Y@24c@0*3iGOph?8wzi&; zNzTe*B)Aw4N6`2_ku!Z9A{aK7of@9+czHWwg%kT`emUQTS@1M>O@j9GTwOnmpzelV zjknT{lO~z;5c$?7rO^LRl8$we)3qy!9NojJHVVfu)%|zYAuoB;M-`;>TbRaIi236E z;7rKbF{cMiKj?#Z6HfB8Rb9O>(i4Qg>-?b{?mQl4QMQ-05p?oCKreo9CHsQeBg4d9 zD)0VdbFtx^SK!}C0UCUVfzk!}91adCu{vg@svftUt* zerVRuMmLFOD!H^c{(LkjdZ<=gR<~vvK%bbnwv?zw*N78YV=YYS>d0szEcT9obJ877 zS2Ppdw(}ZR@rU`2<(YW)(uYQxpU$}36(d-4^*kmPzSn;*mx=m!$ z<)7^puKk9TT$Ydu0EJ#XD@*sBP!BEuS|!RC(EU8h3HwLXAW?~iaADLz%sHydY-#=| zIx(Ebn@djS56K#n090kh7F-D7X9FO$J+0tzYZVD!MLxhlaw6y^4qE?@0``Ei``Gp@E&lOuP`Y@a#1Mis!Dz_w?UUcF z(PqjpE|D48%$ig4!HQajV>GGd4{)vcM65`H+Hpt70;?waK^rnSFc{OaA@5<wfU;iHw$~a+eB4D*pX!>hb0R@U78NHls6hYp}U18m*Aub@VO^6+^(o zc(;s}=9JL7?kAivR|naTLP9sUO1Lrg{>B2Q-mR69`WeyBJQJ6+N={i355)CP65?il&q%~b!*ZrgJP>1o)tJ0;>&%X1&) zPgnT2+cqom;=-aazHcGDt6YYGAF^;TMO(I0;J$A^GV7sluv`S4~o9*k$R6uut|Rhefj2rjy5+Ht?DMNJUXxiz;>FHB{Q-P0nyH}=@wEeL>Up+jqE=cSqTeX6AgepU}u};;V`bP8a4e; z#|I1u%=>w6dqps3ZqhO<-k_aB??YMF^N zPew#;I}nlX-^+5n*DWY6M!ZO`x3wLKte8^UXpRFXe4o|h!7k8(W=$xiC{c(;P$~bT z#q%-UL*AGqhhd+;kRSWO$UL8Kos+cH7M9s)-K>^6Aat~D{_0nkt74fEVFs*tZm_Jb zvT$+!)qZ)eFFow({LWC7TWec8!2D2+$^Hb{gFP$bw3CB6aLZQY)XsIe1z7-9)J7-V zJjVE|)BM3IZ!a)CbR~hk=KP5@nI|IV#$*o%^t#S!_HHuKimk8;xVab&TXhm#j{9OM zGSrT}oilmT?1!_pD8s-f;CfrIvxA0qwDjaI#yc|>axTm zL$OvNTkT`}2Q{VUqj#{&*pDci;Dl5no+(rSV{IU8j0NDqAYzmf$7R08Tt;J z&*3>!=6*P$mTesCySFVeTz(p-W;asWB>k_hm^MV8_?R-pVb12xo#}51qJk0{hI(yb zZZGn?D*-1t|AE!P@`lAl1d2#Bj&K8C{!v{QpkjJ{2SVZ=!FN791Vq)4lKz*>|3+Ux z#y5`#O_7vg39(S=pcFy=X>WPVF}iiH3yu`>J?%IA8>E;XBP_LbTiq5{WD`gBu()Y3 zwbD5<$(m@iS#ok>R`qV#XDtYs-7+eOj%m?(vp6U?593Nptv^tESBEdoQq>n$R6Fci zM5WLfKCH9%OsaPCd>{(X>-YCu0s(a%q5Il^mwzx-2pjD z)?F;yw@gLqs1W8Qj73$o2tYjUZ=_{6Mm{YxDj>XG{@1Ap<&K=cxgVY-3q0oq1q~z3 zMKwqRIO#~bf3kg+F=$k5lrp4*G&+JsM+bi+79TqSeTt>4Gtp^kl=d&1IhS8QY#Kmg zZC~FE#dK)dTU@?g@hR%ICh3mu`>d{LEqRfiW1EBK{>{zz@996)aS=TC6`RG|O9t@O z+^MKBd2fK(6=X*1-vVkZx8nkr6$V9`Nk&dVYMN27$fycxDF3U}=x9K^8KE5`i8KA3 zQKdLb(M^XB{Bj*IGr2bvJ0zo*T+1PUUyy2H5c?J0k@v^D+CYieeW-kY#x0z1$-QuP z8%Z>8F(TH)gFXrEzFctY<-;Zy8}Q#Qe0`3fG>Hg{nxFZZGA9>A`EXjIR90MbjgeR> ztt*UmoQfO}Cn~2aLh>t3C_|;mDxx?93o$LzZyJs#{u*l*$@QCO&-2;GpnX}blvhuauHK5v`28)>MPk0vgg6A#Y+a6epDz5e@?m5o|8M9nc4MMMdB z)%5SjERu~G|C%s86v>YPD|t}I>()|QMCPd%%E^lCFNWmcBdd3peFXz!B^hR3DYg9k z%hL4wCMUz6ynXhwBqjcrRQHfQot@SM;nR~UM;Zv9JCD0<3c$Napf zJ+YX+IoD+X!Chtch}php3J3d0W|l+jv-obgSR*`pS5JPE)%BU<`U;`CD*cmDxv@xD z(nSVyE$7P847;3$oBN@&t!ynE?FUor{EMZnC~%u4*)&|IjXG3rB2d|T3)j+=X2Dqd z(k}XZ@(?qEt%Hm*c%$hM?Ke0hF!=0%RB@kXUwIrWe@C7b&)~}L9D}tEcs4*H#lQ1% zg(~#xOZff?vivAI(3bH-j)}z`Or~?=0Nl$-QbLIEVCBhXum#63?~eggN`CEo8Ak#r zwrE*Sb7jh48>~c$D&nnS+#pR>7=*~z_o%(oHQejbga)&pC)A}orHOstEmoBn!c$!|MRHJ3itsEMetvbOO%o7pnKc=tU- zY0VpX?-!{kz15*6mDIlCeetqL&uCS4NHibj*H3D(I~Ck00`0mSifF`ysM*pvFVbJP zhA`9pjQqia-yd^_LKC4b-iNWS_Pe&31|0CFIEA{#p_$HqKl~!==0O;n2K{`6$$UCx zM-N0R*1U|;yEJCeL5}`C><)3*W$gztk7NrYInM&tz839$^ASW1`io&{!uatez?AZS znB--w00#e-OAAa$!Y3jk6zPfoGpP_Rk$TV7Lg>Tt+GB^;hLF1mwQHVO{kCZ z^Z^#WFta(7nRtUpU{%DoZ~OYvG*n)rkhYPpVWsR_ilM>L=Rq}k;innmtXnsv`73yq zIVg850F>Y9Yl&e8=&YSdya8#`xOI;y^>MGCq4ggQZ+ww$%k1X3fKx~25lTWHE&9joQ=;-a_BDy?9hVAXK{wVW3>&r>C-zuN!PzS5> z!{@eN=X_mMF>h|1s+*0G^=m_TU-5; zo&ZERZ~z!3;!#9u#(eK7UTVtId-;1C8m+3TDGJ!xVEmv5{SF%qF*B?EEXEF~wnVt0 zdUf6rigoDdYAjHooZGYC5@6guCZBQnW%C1aWTx?Wy7z?qlk~oTfC5zr5HK!60|4yA z+k5pX4=80vq4HY}+Tj<-l&g=!r3z-wn3mjF{!4jqz8H|6hcGr#edy76)uXv{H0ml- z{mx(cm{S60Dj$D9i=js$59opDcuW6SQOnULjw~*nI3`&?C#l)}kkov_weQbPic+=3 z>GKVV5PP0h)bFzxlE%W0=!ozubWEG7_2lJmSv)#vF6pmmG%OKZuP(Or=#?!^On!fe z%nJ?9^Y62K^74tTr=9Sei8jDYaCyjuQI^|eMbZ0K>x_tyu{2^M^zy{rjP^_1S^kjv zBtmScekY5UN|9czc9SlPe+s#BlT7D>^E^g!d9k@Kt4QwSM{ z|4JS7u}c)n@RyLDzWyYff6E3bM7(yTr*BYerq|Vc*-zZ{alrzMgDW%FPQw4Z_b6*Frk}zK^hB8~iBX|B9IoGnt<{0%^*H64()oO59i$S|0;{!W zesZt8T>jT3N~wMza0NV_yxojMa7|vtgO|?3IPgFsi+NTB7SewL%P*5`3ve_6OSRL_ zUp$ag8(i{_1DoGhPr%Zxmqt}6Ua5ut(XUm^5(J+%&y5`6-px58FhWlp+4(d7)~bK@ z;NlP1$<_^ZsPQCG?*H_gK*n?q7s!KW<%tSqZ-9L7DRU6wWF{rIF4C_hMB`#Cip3m$ zT9$H~v{?SAkrto-m3g!>ekbU@?_WCahb`kPM^_tw|BXHjA9DW#Beu0GXj{_Bm z?90yhqF0}+zwyj%0POWNx@BXtx2G5(f+l-qSzeqwG5+|3M~V_1m4Gj{DBBdLy(vE#2M3bY-Esl$wAHPdt>-(cOgQ2irYA0pfPf%jTe!Z}q*iq@^ ziW7%;He?Q4(V1G3W4GW)Hhh^=gfT8`b zYe!m7#F8l0MQ7-Z*?9`_nd*r0gM((8)vB z^VJixJh#}vP(nKVA6d9dDrIO@V115)Q)JrUZt*6|b<(uwt*yO}u2g62y)5i5iT;E7 ztxZE8(m>E~WKO=-F~Fvy0fjWp1zE8+bHSb}XTVGMt1SDomO8E=dvH$%p7ae1G@!YT z{H&pwt?4B&Y}UtzV)c0H{}m)QQE%5oL%h%lCSc#mI^6UAQEOW_qMoUtOGm&9eE!B} z;D(!N3Yt7I3kcMGu1ULq%s$~NI-Jh&|MSzxrqAhZ=AZ%yYHi>!W?*NNS@~E?h+QfO zCbo6PeuJTeVg^&5otY;a9k3ywh;JBzPGJQO35I4$jfzk2quHu&;U6gvH}@w(OLi|hwr9g3lpBy-qw4Ly^+=<} zv;fIXVP%m55R(2%%sj#S^Qaf$oJaBb!9=E-iZSy-Apy2NTA(^KJKvcpJ^m$Lx2x_X zXgw2h6F;m1@=}S2iJj1)*1`l3fXIWwp4l2J)L9-NhKN_xYfkwDvfdDI(f z4B!EUj<06ud;D;3bbXjR6F#-QwB{jy~Np;l>*#*`*Q>&aG^9}a6= zcDA?o(*uD$1#iG5^Uj0(Aw=jJR-FG<7sBJI^pAtzJ6-;Xth#e~$dmW)nZazaLHBVd z+kR=t5s-P38v$>4P5ok|+q|(<_krx3uesI7e5W`Z=ZCh=lW}W+U%_Zxfy~K?L#;^4 z%-k6pI`jC661p<`$CXpO30|bvTS86Fn9GYcOD;HvCutJ)$I>djU);2$1&p7m^e->d zK|zAXgi4rp^bjG?@#^U2-&&8l!7;IUXPWkvSCY`PoZo4{WaUbIz_+yZkL9m@t99)w zHQ4Lj^zY{;ACGaU5^OjRzdrYWy4}!Am2-KzG>xjv42T#(40vC zn(NcntIfkliDlYsD&}dW(89xRW+5@RPZBfo=}LOF<;EOoa;niUc>qS+)eji&nZvZO z7>R@gYk#NOgVW5md4Y_S7f>wc&sYRM@6Sl6Lb`{8erA4Sk4$RDO}T9*!AV$6M%>H7 z2YeTiVR-|^=n#)Pq<{Q@9jo}jY(7Sf=fIS6ZHa#?L$xSXmx>jMoDo(eWw$?_H$6!h zq*nhD9P$(WQ`|YALcx8~=`v;}dA%tF|3eioEx${{{XO@;X+WCH&g6zHWsT6Ih0{Qf zZrO%Xz2tf(3fgiBN7=R>dovWIq7;g2jS4Fc!-zdRs|54z}1p)KV)gs;df&Z=V_p$)%rc_B((xdc7Xc3^z%}= z$n7#MY{e_W1(N&a@}ghr`oh9JDFFH$3(+(x8w9Pgck4cl@wQ2YyavZ&XDcJ}A) zbDurm=TnMugpVV?K4pt6&2>!K5SP?2ELy;n5kUXv0(`6P#}Q5ZHDLiK?tDe1TC^!J zBIOx$Jdv@|5u$rGk5Fy>nB9a%!g#r2nux8s&ViH7M2a;Qp@;NuRZfx6*`xM|FbHS0 zc_Tmh0`0n4)f9ieg!olsQNbpGq$fN^&3ib@=wetHXR3*C-mvL2){ol~Ru(6M+kp^k zxp~^iLpuNtS(>>HJYCeomBg?=|Czu4SG9w#2Z=F!NB_tR?3WIymQFo(Q;u21G#~NU zyZFXj|E<4?mlVdhzOY&t#MS0|YbG}bRZxyNA2k>|``a@=Bp?~cUWb{z-w397uaQx-*0b0U6`<+ zk}(;H1XAAthFJBMho^V^wS~Z_~p`Egv{`YjoVKLWWd}nK`Zx{qBom?5}0ef8H6H zAc~$pHbMNa2F(MC$2Y|L;2(nws;k>W261l;XR77hH-cgQgZsnx!B>ND+%RJbdgl+@ zzBa%9HXL&G_4(^_xjFi?%x?s-p7UWqd<2Srul*xfH6&QV_VTOq_19pkYrDa30e$5M zwFxxH)YD9o0|C@$df^{sS&MDLsNs4^8O2}?Y4KNC^P_xBrxC$|&5IbuP6B_Yt#D72 znwS()clm8B)v|vhtm9SWkKyEJ_lw* zt60YH1(AU~o@h?{L9Z zoEQ)}-XmZ!?O!LK`l>SF)+XfF@)~TKDWD3i)uW#EdFhFUsi;FqrD8q?l6gF`fqB9? zk3k#%hbs>HhAxgmb2zY_Ij+x>1KT;oLTOrEN>^^k>+u)p8@p4hLFiE~1@?I;ncydf zp$r^wIwJFxeCNGubZnB##+L-2F@^YwS`%=L67Ug?0fO;IBR_$Y&F$%qPC!}Y6xXm(A?I>hx6g~= zC&zifF;}S`;y}q4J8fJ#R7%)NUo8tl}p=Z4*^62 zQBdqx>C3;PcTXLtpwWc(ilWu@gg4`VpF6NeM*VO`PHh48hCU-ygXV|$Qv-7h_XNl#a?a&hrv4$U*9eAxrknT$5O-|kG%9%R+$4;qFkiMnoa^DGzLoPod2U|2s zy1f{D8Ci!fPm+CwP8F!K8MDGuO00#!RFf#VPP9P^6ci-~cVt=t%u9n&NLs8ric_5k zrInK0s)Y>$L>fgdWz$s?3o@*fijZfNY$Zr zyXEwk0?hB{eP(?$cQg8?yv!^2TdZ2eA8G79fQWgW!^y_at3XU)H=if0=Fr=uVwj5q z=1fhIVic)jTz&D;cu14|*UIt#{D|iYhASHfnu5xmNn1$!XN86bOet#u(MBgID%O0+ zD{wBy{_2^&-O(Hi!0oi)nl!ECIjj+>xxLgj0JVr4sP*FP3j&)69<5Fm8z-6*l>A)XqSM`UP$IlMeb7kN% zUBM9I-N@kwET=Y%`y;;*RNV;2gntpb-?WC)PMDBJMB&+0(PBq5@W9mSDk~RinQwpt z)$)GW-oraVlt`TNYxm3BJ)A1HVk7Fq8MRm)@Q;P?FaHRDEH@^diH{faQ-+A*g~etI zbAPH^0fVUW{$K7eHA-3=lmcO^7*4^~pjPh&fyZf(q04=$=GjVsdO7dvYFhREv3|&3 zU-ifj@8#c;nfwh{YSH1M}C(5nsN@}W}n5=ze(9#HoW8l$3$LY9T*yX|!P( zh@H7WE`(GLee~G^7pCaymWA+x0L475yz_-`UzOfOgfD~2_UxAQu?|#OU7mj(`>NoP z42Ww+$nRjm?EYSm;auQ9pVlN0!+nAqLSopM>B$|{@Lg*CUDK(pnO5s>(|Q@0VZ$u+ z?=tMHV30~*?S*Vn*y@2HXL77-RX(_`c13*6*hU|d!YFXGt zKVIh3@bXK`neE%V+voQXjpYKQYXk(*=4U|mkn))tNwPyJ;SH(I(r)#Q;1j>qWgGAZ zNmH*XJX7d$NBB=6pebET4g4@|vHMagUHK7{%K1Acl3?Xook;24P5y@!dCf8Q6jj8n z&ctdRA&!gUQtV_Gy+6n$CrcM{MZ3}#^Nu?kjnL6 z0ALV|?~AYRTj`D;K)&R^(at&a?}z^2g|1uRA;q`bDnY7GYnspZSN$ja&F6i2M~!Yl z&P-k;Q6vDTxdz{(RMEDTSlPV0VFT4z7`w}(Vr!kS43jNe!-~!4risgIW_jKZGt;g! zydJ1Ootyz)fBS*d(jf8dR+;i_N+``yJ|H zuUCeTYwfxSbp80vGgt8@W~Vcbi6V3La3fKlcxPX1Z+8-$;`W=6Gn|2YLF6oswlXfH z@P`AF4$@be>_|N}=`>+7-IM&ZB3{l0bci5S?4yHBU|OJCCISGvzXK398qYfh1Dc># zS__GM$V0SFvBGbQ#gU!3{z8LV|4;+cyotFG_48?++q_A&Nzgc_23 zF*frfB<7RvP4ZWAe$o3ak7_AUjj<*Ki)ZJNIOc)^nq)AAplH}X+!jQPkM?IOb@ip` zO93`S8vZQLq%!KG&~}4(h2G~{0(SN7Y$ zw6&PEz2kmfBX|BRn8z3|QkAn(c>Ym-ZxKlcW8?04^Cg%PkXeJ#YXsu>D(U&q%3|YF z>iCn;iU8(t%eG$yYONC9=pfoWEjbF!Rl(zQuXabYw&!>0bbbS+O) z+X9W9BC%DpPH&Can(48S=mZJg#gG=Ao5g(vyX-oI+DwdsaY4N#6t znW%l7rimW7$!vc4k39JFbyz&&<0uNL3H+j++71RLKR2U^yi|^yKk=#;$p&7 z8YiafDeJA$%cXTC8P}Cc`%nP}Vr5>Z$dZZm*;1VuY@_aW$w{S5`J?Z}dtHFI(NQJ2 zQg+mX{`j~Exe7t>$v$nSUGc4t<0*QEVS2Su>)FRAEfq!9R+8s$#Z|nh6O5^T^J^yI z50CQFBeG9_UxO1li$3w(#q%D}fU&^nVzqYwt`(aU?+GIEZB7;fc>evLHzOjgu#`&= z6)XI!MX-TON;;~Kt9Xsih^?KfFW>nR)IF`>pWjdgb=5UZKTU+yp#;X!i7fuko~)wp zOi7M)K&vgP7FT)_CR2}Y4Ti8K=V-Jx7eWt<$FM>p5t3%&`odUHghvSBbkj0~)*r?n zR_CAEa^&LtW(eDAyC6MNrEEpwDSg<3j+?eUfSs%Qc-@Hh4Q`?5AGE^w9oU`98?fv& z;auHZwr1P3oF)xrol{Vs-QYjOmN8(t>lODqBF?wpbeuMK(dze_%8^9okb-J(9iHj_ zq9Er5$Xr~e)p;UuA!M-p=EvI&H6%PqAU~ohaGmImhOucPONL zOpl!c=zKG~*Lc<*1!=x_^_z3?ZCOY`Y`IX!-Jn<*0RTNxtj&}?H6&4L~w1hz++O%ly_@a@J z=Ld;DvFd@4pXN*A(Nx}>oy|d4rmOUsC}14JgE@Xqt_N{w(WoByDjTSqaQJgaa+qcu zvCtv+M;&vs;_^tZ5doiQ*gP_=mBkIEP&lfC43w*vn~FV3eWmWCXR<)+%@Ahp(wqmpqKOP@Q4I# zG=+}ZKw?l8(g85$pBTf;vDSf&FMU9YDAt4kGJM1ZHkm$*&ppeQL|7uvcG1|i*VV4- z7C+b*{^tyD(}|P=i0|+advK4HaRGc~@@GXbzTYM9Q?&7j zZSl985ff@~QAN<~&iC#`s~@2HSs15f7WjS6KmzV#zderv`h9H(K18#$&Ol^MqT(0f z9@6D-M|s0eY|}-AmF5mi%7Fkpzcs_|lA=Q;4Um696^Xtzj?=aNw1ITd+tFU+zB?=> zhJwyE^^eGk!IJ6w7r);EBJaBEJW41!q;7kn$RMwo^F&alo4=+FhgYcNP!`w*coC(# z2&8K>CFEQCe-d}}bxUe&fkDp0!-#(|7Q+keDIYi09YNiVuB zBOOEsT;1c1#s20ly&RiEkAIq+iUg(G^>5Y+0_^ES`2}~s{{EZuI5IfQ2S*` z=z$5@5ZLLc{HPC|Chq(4)y>riY#geVIh;WRol#$!)xa23mh5l3tJFs5Z8cSAF*_J( zJbtKJ5VUWv7^Tb+d*c9Qk#%2zvH@RWu-)DtA&|lxu!wSASjw|_?erI7?{$KzPDSj^W zk7h469f=XhseqPVPAAm$8IUhY!3_oa>ML~Ry5liP|I*~J;Lbd6<`~@1R ztc(a9VlA)wdo%|~obUYs440Na-+D2qWXkRLOr~a)0RmNVs^xggjMAvD>|9a*UPK!d z0a~h=Y%Pv8>2hMt&UY4MZ5$t(6ze`0PuL9qYF?3QeC3)7(N9Z9)T+A7-`4(bDc3C$ z>ghEb%G`+~x=HjZOGb%14a&l$Wd8C?>YnCiK;v)5dAf>9c*g2c$K4CUJ6O4d*WeuF(QH@Kv#67^HLYayKd4b(yP9MLA{%=9k zG@=ueKHPc7j1|B2S`-mTrc6oTW9GZCMZ|E1@ota&cHjF~?8+@7F4Gl8hnRD>Ujcew zm-^Y7R4%+4<1os<8KrdLI$N8rAVHKz4|4EtoL?0U-I7RS^^;h_q>GB!_*P6&@pg zrjM{eQ(}NxlIz-`y-bHlU+&4V_);1>i090F_33~Zangoi?NaQf{TZ^w=Yu8%Ne@wA z{AhxTjsOJ4c$rv4Ka{@w=7MNlF5ZV6pILl7b|F}))00xVt7Y-0w#n$O`q?D)>tRB2 z4O^W9r1~fjajD(bX>7W;{JWjIelr%Y@%_0r%wjX-mv$WF)Zp^->b00n#tw|iO4nyA z2AJqDq0i~$lw@4_@gys3ipJy1Vq=;|#ocjOya7Yr8~*;not^TDRHViq5dfcR-IQos zzOZd45HHfpPsRs_6t6=>Q*1)j6M*9#TDAwqwJ&1A8*D0V5$mPHdGYW^;Ld9j!4md? zM2V&b!#UXT-bXTx)+&eFZuH8wq}t3uW=h-|^|iYLQd zDh_HBmFv~&?yZ4_yIh@gX(EO)J)}=h^C)_Zk&;YGmr#HxD^AC#J++cE@kYAb%?LYA z)e7Nxn2@(jc|$CHEL8P zUVUI<4thkQSeBM zr0(dbFko9qt!S9H!I+vb;-!j;nHsB0=+rQ5upYpb#$9qj*aXccy?13iwztfG74hn~Ek|3MKQM4`IuM+xPLV zygC55eYjs~wj-cRE;dIz0BtZ0dE;wvJWK{!vnWJ&t07?i5FHW!$Lc4Wvi3sXDTooU zxYAV<0;EvDgh|xLc;=Ww$G3z}V<2cq`a4jrDN-i8bG3|!_=HLU&eJ_V@5UnpNggl5 zy;^#9=_kbBpz%iusX%pGRFBk~50Wd~N2yf!E`RSw!&#aM>b2t1-^m$z&F*U`$<(12 zJi=|6$4|1<(F)%TVlaNaXVi^2J}I$eQfY%-yqVg9Ezo?x%eAw_#1zEbr%oWS$aQ%0 z7M!@<%o?%iKhlAiEB#(Z=PR*DHDGzg0PaDxIBHcrrZa#b#Grqux8S5K^O4LC)ITQz z`TtaK1ZT07eHGx{`zD5>Adp|EO-iCHAIdVXt5k>G^>SQ)WwE5(!s^3giot>L2fwi-wI;R;%Py2m;11s-T{X$x17EKk1zpgp_k z!G@d2Hw1^urx);A z*RJ6LTq1d)il5t*b|SUG=$@<8q(ua|ljt+zSm79~tuv7kNdN-_iUV1CMIB;thAz42 z-m&GUpL~`Ah$Xx$Q~d|Z#0(X-004aM(m#3Bl`&sLYCkx8S7%aD`sF(WBX^%5%Y-ef zO^;aTNx;^a0)m{WQcv>tH364kAv!3EIx=M2_28~r!KI5pO9vx>A<hYDmg zC!3eeZPb}!fF(8wt?)zdDAL*x zV1g2>+QqY2SPdz&QPO#|=NP5+Aj9;&ts$y58mW`6@vjT6;3WRQWOwuT9*d9OP!hMR zL`e0kub+dAiq>C_8cl<{pN3)wVb>77)#3`MlovU8Yi;=Gq$0l#u`*!vK4iW=L`(T2 zCi}X-P0k{$HrhK(1FCP4*F~{<3L*kW+QLk)h(PdesBvf0GieWioz`0%JuEt*uHj1n zb4dw$s|jvL5?N(0hJ`er7o*alXH9DCkr{XeTQ6XqxP^CpC^!0(rY6<>ut}N<)i;OC zvE>`mCp6h)m5PwrRHE!Pi71G@f~CGZy&{q8D=Z6HM;E-Xyl8w6m|Q#Dt$Ot4{IfHl zg=$spNn>}lX_X1tX6d6YGqrja2TDF~NJ+9czYSlbSlNSlD-ee(9e{AHe)gP|#RCVM z*2#qgyHb}WW80Yg17v6d4=BixBrCSg<+jfv?@@Ij+UtL3(x+|WIWd_#^m(tATA0Usv*8lyt2+ z82>=BB0;h){P$?{ zEl*8Q>y8se|3j!$<{QKae4kn6acBRx_$+q^gXYaL=tOg9*Mrf3qDTWI56b+f^TBz^ zFya6Sc}J+^srz1>L0T9UZayA(vlQ7(!oP>2E2}WPoO%(x1xs;SdLFrBq}n~G=b(1F zihlK4vo5DzWUWEIOP99ghu56S_d0SG^|*RQ4-)3_iV7Xom`z1X&Li#L(yhvW>d;68nyuy$G`MgZ6~(VGx$CSeFdo!Fcb?8+Ze@*(Z!*LmSbI;+@9J*=6JR- z#xX70+QphkuoG8bDuV6*U0{uTv4q6V;;nx=(_*~aDxHaOv;OseF2Iq&=W;}Ar#FwN z>2%0#v<5r=cqs9tVR^gTQmnFxrj9ind%TG2CZg7QPHtK>Ys4rqYK5X6`;@a%*6TYH zCWGBTotnr*Rqz%z2R~}L(P=<2L~S5eBxXDxjEO~)=M13{C(;?#ox#(Ud_)>(Hw7Ec>10W@H8tgJ%9KTKo5#1!<3Q*2HW@f;lDY&no+F z4E}!RO8s$t9H{QOMy`C7N=xO#zC*%Rj#ug79oX+R^~R+r$B%LK`&Kf6gO<;@`Tte* z#Vg6f=h^yA`lp5-k9f$B7}2=97xm}Gu_!KtgEc+>cL`Zn*iI#JHvZf&Wi%@< zt5A7ME6ajWK>1Y92nhchLnYj~jn&z`iOC*MhA8M=@d<@hk5?MLTFnkhF z6(P#7i(LadR(;Zp9O5iQ!gf;2H(MHx&$E*(o%jN-0xE?j-zP{Bbu^fVaDdW4Jg}{m z$N^O!zLqt9tzXQ~rCUqzNay!M1gX+M8X$t_wY0@wfgxIaq!PTnlhw?ngaMMOXytt= z&bW-V;SBrxVTEX8eIs_oY&q{0DUHG)k6H_o-A&6pqedL+A5yQ5nKZ5L2*;q~qxU6#xCzZxx!tpPRtvctkKuXL-==DK2DoObM z)Bw7>ZF?|AhXuEvGoTju?`v){P){>t8^FvCg}(WTzsUUX>weUHgRrXt!v_t^8$wvu zXLS=W4Dx?Bf?y4^&5LBk7qhk$nf--i(c@u*v_94u4*Vq1{Pb?54Du0|z6J8#oJRW* z8+cdo1pa{*{eT_KCyoU2e;UXJG^nH|sWIM10N_kN^TDVP^gnJgQd* z##wtz0hssZvBd9;T3|EpDcJ(5_*L;!CAM-3JXP2rebU!;YF~GiGa98D`kN zMFsps2Y@E{%^KZ9o@03hFWDBnXKQ~v00?_1U?;iv{zD*%?ss`2dz7>IJW-MG`G@mJ zt50<}SYzOBjt_B&tDKUR3t@ze^L^H5DU;EUx6AK~L!ghR@Jq%4u2o0zNa*IexPqkl zeyekLZzhwF_-Hu7_gA|F6vXDm>;@BS=@fR2)4(&EjL zg&#vLL3TE~g`0V-+?3s49Hx`+GKBPWbj7uSvqe7N;vttE!bP?*H8=1yG0J49l!fxVxqFi>Sy>&KjOu2ZeUjQQ1=eFf)1r8*{^X9g`8U^JTd;9Zz8Nt) zZ8hc`r29*vQh^|P|RTX ze~HL&XK;8N@;7)?UX_gx=0a1ObHnZ8cMl&dW12JL9AgSyKhaSKOOrI7UZW8Q#UvY} zO!9gEK%5bOPBWxUN6}8}KTK(S)1lwCSLZ`!G64*x<&wkmWQg^+R?kznQKN_yYY;C{ z&Ze!MfxnWX?wcg(Uy2vhM8Y}9LP}U_Yu>uxMp}pN$;I?c2U|;hEcRyl;!e^9aWb2< zK@%i}(m`9W7pDfcO`BO0nvI${?Je}?k5t%0bcQ1*(ldT+G3HWt3KyO8H*1)TqO3Su`JxV@##8sFqCvnj*0>km(V?&#ze*vS1j)v@ z0mw=cF^h|{x(UKS2b7+(a`N{m8;X{!3k8i6zGET_$SpS$d~n`VDq!_>^aGt>8iKJa zGN+}|$GMML@z-sz#*(^1jY_C)qWe^Gw&!MuV{wbLh0of>B%)ypq!`kg@d^ssUEFZO zT&Wwvu6xoK<3LTUF&yD&h1Iz%G8(f5-bI--=24~zu6!I786pS~msMXUFOo zx~K;J#OV2MWJoIW#SGXS>`SunmPO3L8YhlL<-|;mso54|g}W>JEAS-4 zsF|AsRGzCKwa~8yVdQ3LGYd6|*bZMNlc2`+D~xky>-l@gDpb|+v(w{z)--sNFjKi1 z;&HIH>Qxm*TyeE?O-jd0ye8xjC78b&?uP+N-FqY*A9AE!<;a@@e(*$kyaUBdfPbU| zG9kQft#4C!Q5L@vsqdRA;KbI2ZNYSC*60*A*#u*tP5w%8LN?@^>v@GT)d=ccALQFE z{lr0eZ=D3`)EVWjQ+T_+s<3vG)GbA3WoA_95DkfD7#AgR*DeaVo-WBFfCpq#kn4@& zgCysh3p`)gHCkYSTp4-ko?<{r0wo&bqsOy^WKovPV~&Az#EH;3jA#uCNdCnd*_ZhZ zBy!T-?KBXE%JNOC;uaRZN9!Vsio~g%p|hcl&iPU*pN5B`w$Q5rhwJ6%Iz)FKQGk*vnquStqp`Aem6(c z+pQ2i79PXDx0OuQ>AS>1X1eDJPmLt>Y!$E`@xcofEGi_k}sSx5u3H8L95 z3Es})wS}xZ^$_q#)Y4jc%#n;Gv1@cv{u55CKD&`4KZ~SMLr#x!r{$a$B`7^C{OX3G z{Rl@G9ZU{ie-(nHNa!^oST4u{E>aK+^9QWPx zQgUCB8>>X#|E85Pls!C-Li#}C`W?=oN4VH1&2}0aMHBjK#&`7xySd+*qqOiDPq37* zh|}JUE^~~s;^6ijt)mdj&5jTqz3s;&LR{wrRsSFnN4AwFF0BgM766|k0V{4l@rSgBTEixnumE7{>xTa$K_s_rSy3>e4yZ)o#{+dfSqxW)8d}CnMDgi9dSc4XZ~H zyX5!9fetr}rzwlapF<=8W$M!ue3^JT#G@Hu(p^rZl8>sQWT8Wloj0BK%Mfx}x@_DB z{a=?OUb&Pp_#-k3%D6O+c+_A@eB?Xx-%zdaN@TLOY{yT_2HY(v@37{OdRo2jo7Bw_ z4TQVzhf>_xdc~@=tdG<_sIV*Uas{*46HpO$@Np`ip+r5xIZ8S?0VnFWwXHCY{g&fl zVxrD$;wrK&xg;fPaA-aL07dxi4!EsQw!U)nl?}2JxE?nTm{2gK>U@?FE?*aX{@A$$ z2wi_I@o$Bky!UGhIZg*!H!E~YP;FZYef6|_MbP9KjPj)-^~Acjp*H5fI!5%|IyFky zG1T}S+LUL@7Oi0QxmoY_X1)X(ibRY`4zo<9RWHhwaS*vrb7-a9tg}jgw^Jn0Nv^fE z`GuSzeSjOLO^xJm$FGJz*Niy*wzB%|Ve1QlOQzwS64RLut zH}fmol(!kSikli1g34_el`*~;`o;tX*!jWHx7jI|#Lq4LG7y;s%w{_y{y^iJ*W;K5 zc@rewbY{e>#~28+vzm>|0*h%6#Rf@MYMWV8B)Y`GB>`NbGtaSWxM57LTiR|FkD2%o?LIQWN zFGRkIefudp%%z)Ram$74ckq=-;6sdmt_@67)N^!R0%N?%2*z5+^3lH815N3@@F-MN z(_;?msg>jd&XQ5QPZnRMdrve|ve}Z`nf}kg-ku!1I?pogkM1=T(w`r}Kn*Z!~g1t>RfIRbpo{I zT-;hlP}i7dXqthRPYU0mB~k{0)u=Cng>bZZ$+kCKX;*yE=BARp{r-c$`SL ziA@(aElxW6JI0-d%8Fa%0r5#YLNC?j4Qm>l;VmQ`!q4UlJg2&h4Dyd-G*_5_#cEh5 za%`*taI#;S!23o)OV=k24JdWtL8AgH)EYN{o)CBY(YA*j+!_(_Dm=ugAx3#imKmhh zHQ_eTt%SNF+-1=eFPKsweonbf`0zdCqn96tCGzTO6MPme95u8rtPGS!H;4Y`5^~I? zf9!)O3Pl}3psy7i|B>c7zGwND^oWqTDe+LE%1=Ok=Be@!w)!|HMP|Y3VeRYkm1&Cka!jDv)}1oyar$w@fb-D^GTSm z8#!WqVl2z*x$}}9EU|zQ_cKktKP9MLQf$SOvlomPicZK$^1@1Mws)Y8OzxvPhl-&V1N9}(6&Gq~Fbj$)aZ&5(6H&BBM;cW$P9c0^S%5X0v z9^RhZnOjh3Gos9}D!iSu5hkxFW9t?)uEutZOem%5>FPnm+p)S=39K>wUG!`Qn{nN|{Z7nX0BNA6E@}qmPzJ93y>qK9wX);v$oaJr+Hny5EF=Zvc8=0fV@l z*8*Shh_?BsvW9i0=)Bhgqj?Fp8LKb<#Ok9a$gnKIaxrrT|91pR-1!E**QOOsqJl#} zl<~Rl@e{ib@byq4hu$J6cRGhbf`xtSKBtRwIlqmbvHA8Eyp@M)KL{ zQEXqC{Jy$OUZf3lNjdki0i=frNG{sF&FoDK2o?5{Alp$ZlYw-*IB>U`d|-37|E0+v z!;!&aGI4MZLqQL_?atoK*&^w11}NDo_yOR{9FWLcUh|IW!>a3ZY`WxOv(?slQ=ry- zv!bFLL?#h}Cr7*Hj@ZwV(!XL+t!Fi7*G|wVEKn+8nKfXhT!qQ6?4J6oSO`6as}z10 z%Yu#g@Ez#XSe(maU9M<8S`jO{UQqqc_N}zHpTq@%8@Q5N;`I0x)w4GPn{f14+JY(N zwT4sPXS}73Sw>zxhvl3HR9wNdWH28aOzY+V;llC$KZSIp1hTB;2!RhsKx6|N^JMdD zSrWB^M?9;OYygQyP$-un zSi~qR8*U}d2JyqsX8YJixV1(}yT#1;Yt7B;K4ZEq8<-4$5SI*7j~^4+7YD(=N;(AC z0T+d;rNkk*xWH;Dxx#>O&t;{+6h)>4!f%IZa~h^dHFhFBM%>J(iXNjleYws+#8r~+ z`Z;p)hE@Wkwxe+ZBR4EzTFPvI1OVRZ$i)6hH7?^XkFpt5O!+8vGb_6Cms*n;sq#ba zK>s8(89cjD+ktCSu8Sj7kqulLN~RdBY2@X2kOQv}Kaj4YX^^-I+`(Oyb@OYsEvd6NVY8Yp!clGjUs~V0NB-dPh4u#wq?JOQiBLZ^4}{Dx8_@~oGloo zX$y=+3|2Kxkr3j@#t|!Z_)3f#=WMImEcd z@Gd1w&Sp={l!zeW61++$MomHbZMy4U<=Q!V>eshJ@a@GWjI!0s;FA=@;p1~ir(Mgu zrjiv02?pzNIqx>rN5Lg3a)hwk%9Bgog0}daBavX^1wb*lJRw zCDBsb>|d|-S~rqA)^HC7!u^366^jC98?grjMt@9%+g1Y{le;D-v)$Z_nV-Qe6&{o3 zhz^#r4GeUi*VTM}+8jM{&cF@|IcL^~jKKQL3dH(0mM&bCHUn4ON+FH1KKd4#j1Q+W zDe^M$Pj0ID3`QGKZ9bwd@%%CMaNLD@{gJPHi9W6K>nw8(U)TljvaDVJ>{v>2rH3A^ z7(K!Y2TYK$VNV{Zx)S)l-S;oUkG)dTtN?np-ABKN>bJ`x+ULl}m9b>?@#AG` zQXO+ZhM!V9S))ROTIMTha7z?^NyVg9z9ZoRqHSk6+iMKk>1;SBTd3v(3{RX3Y5d$v z0R`inlm4#wSA2ibGVAD&7U2pOJBLEO^-7qB)n`+43}%%#$Qk3TO5r<_Aq*BIcs+8r zq-1rFj_sdtf-TTL1zkdM4ELqs;|(H_B}rrMCT1g!`P~q6#VgoN!#H*SJs6J085nZ5 z1ba77GOeX#qZAx|x(Oub8j)(%85uP8`WyQ>BqyY!8i=(C2nxx{qt&4Z3>neQVZiPT z{;+(}OOOt?`Gg1Gj*{7IhLCV-ZAL&ytr|Fm6zV|CI_RcZl^o&6#3(c7R5}5>ie`@| zC5;xm4C@|j&cL>WJWc5CaLEwr$RN^$j`I94JebqmThI(c!_=5zqE~?j&K4wmS*8i% zr_Z)IQ3l}DF?W3J%|t_APLNkPeHZ_beIRpp9xf(paL0=nN*YV>t-VqRw!AM^o^E$P<$b=v*o2{xJZ$#UFSy$Dgyrjt}Fh+!sy9Xt6Pg4y^5DPD2M;%8h8_ZUKTsS(t$s4kLPLVpNpE;P*yJ;;>zEklCb2P^w*Vrw`9 z7YqRS}Q-K8Lg=qSSkXioxa{}pv+9oZh-{yS8@@vsJ76U6WKa{T(uj9`n_S$Ds6{Ga{ zSbXTRH5WY?I-&G855bTWb{G!f*YOH$q@A9US47Ew_W>^2+NApjeHeyqe;vA)YZX(-^%s2g=Kq9+^ z>>@v(H}NR~t@e>sr>(HUz$5+g>5QVJ{iJ}x%Hs_&fq{XM2OK?%C8oV7fC*6z>yE#j z8wf0u`sm?`<50GlkrN z#B3ZdY}!>)N93UE9oBC5%rUv=DWh+eO9O%nmTG}&Td10QX6IRIYX9c~Q1LI%SA%kS zYhxs9K($p?4G0Yc8Wx>KisYFHA4Dx$*J;>PXm#qCb8>=CZ+c;uGpuZ;h>)eosd1>a z5q=DGkw#V>D7k{I+X7A4eo!mgE2X{o95vYcpnrHG zXztGv9?U;+W_gj#P|+!m!qNT^0IFLoLKTeFM9X>_RC)~pWXP%8N|nrUCXNJCNcEbp zBMtJ$vvHd1EW46=dkv0z;lXHz)538>Bc%>9C@p_&+})LT-*kQb8oV;nSGh6u^M5p5 zbx>6Q+r3M7sDOS+3F%b21w-S_Tq=ACg? z$A!E5$0yEno^#GaDk-_@Igd~t}+cirT{=4pGI zV)JwgA*tMl?+UGQH_gLecR{QIiXW9tu(Bm2v?{k47Ase@BA~+U8)-wv1ttiODeJ%& zy4(jpYUNTTE19Izl)>o38S`#!(TU2>d&9rzSXP4FHB(w+#q#;+wQn3D=vg)<*Bw;L z;NQqOIAX4oRQkS;DYglm2Yo+eD|H4M2i2wj{RB_D(|*>Kv&^fec4BAOS@Uqjxd!Sy zry?wOXB>`jORm$Dwj1USeL9Kc%(hA|m~&!E$wUybaqw>=%j4t#r-XH&3h={;~*Zn7eb; zonRZ>KWIC|+-k+*!++N>U2OQJeM-zCecw!raJAtGQdioNq)Y>;!F~P0$j!0c05PB2C?jro4e;c@kbvM(`}}xvEZmXvg2Ua?Kmw70T2l;HpL~rCOW;=k8Che#Mxt z>2*4a0KxmycxVb}G&50&wWv{1RCs$r>96qk zbHzvdP!fu8R2F963NA?%LOOJHO%FQIcK21k96`^3^|}~?-^qkIF7v_DF?Df6_U*!$ zdRLoTX zBW!C8Zg;H&Y627*4Q;RTe%d}ck=e^%bSE$3h3Nc{K`>--X^I|%CzHOc>Df>L-f%rR z+x~I_SG+WQU&BI!RlWh|zE>6leitaZ#2e1NK^w5Zl?i97N0vd+tvhfmgv8kp8X53< zw-eHV{wLkw%gkF_#PG`fa3pJ{_kQK~3? zJMQ8;`F(aP-F-w9d@(tR(wDd_kCoiXO(c=NccpgNIv$S=^ia+XMw@F8I~vesa4UAw zBjY3scSj_z7O9$6u}MMl8XO5qWeQGmU(oBSzdjC^zAtXQEe83XTFB~G%NU>gHF{KC zuVjoIspPpPAcDI8UnDF1L(dc8#<}4%eD00}Y}g!$T+Wl^oGRZ+47atFdD_b=@(c)7K&zdH;fIacVTF4aJPYjnC(5mo)7y?Vh^1o36Fc#7n z@MvvEusf1yUu2~*TDsYYt0Po|MVXg)O+1eK+fNLZIctO0Q8m+uT8y7>RDQI64OFQr zz1#_bStSiofu&Z~r4sOXkHkH>iK^E~0Zx{FMVG!rsLJ|Ime?or3*L`{HAI89n?um7 z75lK#u5`mBC9mx)^aFkhq8D7}ThsyWJG2c0JE)xp-2yO&0A%VwEFcSAs>L0D0%1_} z*tw^F6u#3yaqTIASi!%92kjiwKiY3~*LhFCvJ6-JN470+hck{4b?`xw=3?N!@s_dSFxs#&Gl%U@5-S~i3sv472>F|h<&~z?GUG`g^F|LGxaKjvbmQpF6Oe% z#=w=Q%f*;EY%s4J;+gW9XnqH9sXgyn!hgXW{~y^a6@v;K2H{tZ*zdRNYuL6QLWKZN z)eDs#jnp~2aQ8W&Ot?cIWs9+8FV}I&hlpOscWh z+&A0I8yD22i!^9>)R4MaCk@z^L#GIrc~ut|*0H9$cG%@--~i}3?MI-PcnNIe{O{Uo z_|trdj%ffd4e#yjaH%cP(I5ryE+)DUz2eL9i(val9|>MyRlFKm*f8o7N@6Yu=6?c^r=3`qW?t z>APb3=Ruyv{imcppcxf$AV(s)&cf@{pF?-SCZ8ni-GR?~zpW_kyQ>ZN1wEUKTT)l( zz5XlkqWI$j@K^UR+UQstDK=BQg!M^;unzB4fCaVm?1a*hz+_ib8@al}|Jo>JZPeCD zCLX(#%CO_~-~V`^OdnjeW3BZel=67vJA|+8PJ?8NN$7*MSehAOmfQXrGF0kr#W&B9 zZrr+CUg)toDw&UpNr#k$YQ}5i&yXXx_p_mBp$JlpVyv&+$z>SF|H0CAD!)BudbXML z(6Jh~r5Z`}FwgK&FI_Iu{42ucjh&LCPOa+4Z|sn-mj9lEY)qWyC#kziv7c5K@=GNU znW8p{Ffy=iI80Mqc)QymwwBE1O+UT~VYpt+-tK$gaHOx<#L`Bby!~NkyEu4PWLAKp z#O8OmgW77w>xm zpo1MM)>EgbXSI28CQt@z?%8<3f0+2{X_^II{i@q0ieZhKJ9 zIPni2kN6R|kX+i=#i7Bz9yzZrspU892n~#(Fo*N_A4jhrm}bhqnEJ#K7vL`tugClA zmWooLbdX-=65g%p9i{m?6)!AB=1FZO!gPoS3l!g3fkeb+OE9NH&S0=7Ne1KRs z_M86}#qDM)a9eQxF&!aj=J8r3vXpI2P7eC2hUysXY{t%_a$==!(WneWVt!A2H^13R zfshH=#VU0Yi@J{OrSsdjq`xyD@jY-bRoO?Ooto%g6sdO`rd8M3s5A=gGe*nyz{^W= z-<;GBoHNbw{oH8zl+psFA#u``s3G-@Gz&G@dOwJkR-vaOW`bZ4H3QCdr#*DV{TjQ5 z-p^|>OjqD%M#f{q=n>nBaO8PDWYy%V;SYF+9kZWAph2Ykzm-F;TEFRH6Ho9{3_nR- z)sG@FR&FxTyPL|y`)~76z!7*shOgKe(0t%QZpR|G(U>qhoDLisaa*zeZ*R zEhieeK#A=2=RT5vSAyjvx;js2kI zJ@%v3;ukM9`@g%<`!NbePKJf;WiZ?KHZLFZI%uoK{G6A*cu8A6#X9hO)G(S6_Mgqg zqw3}c!B$YcAoqK;&v(<%el(^2n6owk8eT#oi+KycbSjJG^gK$WQE&Vk3`_NLxAlsX z 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Ξa1|UGp1$~smfx6Ra2=@p#Ljc) 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%0AWXWOn^ZV{=`g>jX_S0QARW zIvIFy9TWfKhF{Li`2g830nySk?HKlVoFONM}E4{sDVZ?s&&RD^*T zzfNB>K~zYjr2NzF5^6&`?{Ue_7P|f|Xqu&A>X$N1riiFV3onpQVHaPg7!G$S>Tr{| zdUDzs_ekxeGXjrV=rSp7GCiYHO^wq0S{53gH}HNFQF~fU8je&W^6WS=ME0GEe_6#M zQ{BUF%EmCztiK7(Up1<|2Ff??Nymy}0rvDG2Z+KL>g9CGsf!@`(P5<-A=iPC1oPG8;ox8ILuuLKBMv4H(6F(l+03ubP9l#-b z&ohK}jwehc0ecp9o@oL!Awx(NP=rXfY#3;-cV|fM3YBCCxy)U5Q>u~;13#Yid8+C7 znD=h;diNHNcC~Sv?eKl4EimZ@9wz2YZYEuKlV{TA0DFR&dHtY=VXs9T?tyb{nfjj} zRT?zD%X?|AX4&S65$!svHD0`Q&UqQ{t!{~%+y--0n9aCW%R`2EAiOEHB{zp z*~ka57R+B6&Mk`N=t>v=EkJ0o{$3O^ae{JCZF|>R(S@}35n#8PERbu#L2<5F(k-T= zP%%*(EKEQO2kVF*h>`TA@Ja$pODp8Mi?{w6^aJ)sr0pEoeInB%rap@tdJhV&3H(p{ z{P}D>ClFvk%3<(cJWUI5pzmJnTn7v0&$8xgoP943dfJaZ@{$jSOC06B<3&!|Gwc3k z+IfxpfygKhFd(pAw(W|uPJoZ?y4Qw64`D8cKxrbB(HR44PK~odekYEqPulP>1eqZG z#)~{%8q+ap^H?8z_U?A2e!V^V@(j$jKY$K%xB~oez!H8ny0GN~s{pWqWRF1AA^5+} z8A5#X5c&ihW76tfGoKnEaWOTdPYFSI?dUhTE&}`nKFKcl>{NZ<|umwotJDj3Xq zG672LJS*zC1;TWodx!_3-{>6QV>ux(^c>T=22Nk7O)i0Q0J)C%0K<(}9a zkOKgQ^c&DyuTz3~cybVO-g_Ya^N-e{V1^^vM(~eUz(bz6e?N!FG5!10A&>$xmVX}; zq)q^K8vAGKV8;P#*gu=;t^ZXE$fFxivHp1k1ac>o@!w|%c`yC{r}IDG(f{eUbBKXH Yj+=HuM$Ce{K|&yk&s1cqq)p-f1==kn&Hw-a literal 0 HcmV?d00001 diff --git a/index.md b/index.md index f8eb59e..ef27273 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -# VERTEX-CFD root +# VERTEX-CFD VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) @@ -6,7 +6,3 @@ VERTEX-CFD is a performance portable software package for computational fluid dy ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) ## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) - -test6 - -test7 From 1468f5a2635af7b15f67d67cc28264807f4c3238 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Tue, 3 Dec 2024 12:05:19 -0500 Subject: [PATCH 033/214] update 12 --- _config.yml | 4 ++-- index.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/_config.yml b/_config.yml index a075c04..2a279d1 100644 --- a/_config.yml +++ b/_config.yml @@ -1,6 +1,6 @@ -title: Minimal theme +title: VERTEX-CFD logo: docs/figures/logo.png -description: Minimal is a theme for GitHub Pages. +description: VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures. show_downloads: true google_analytics: theme: jekyll-theme-minimal diff --git a/index.md b/index.md index ef27273..23b5f67 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,3 @@ -# VERTEX-CFD VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) From 5c93193725c6c74a96424e2637662d85a5c985e2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 6 Dec 2024 07:10:42 -0500 Subject: [PATCH 034/214] add introduction --- index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.md b/index.md index 23b5f67..935face 100644 --- a/index.md +++ b/index.md @@ -1,5 +1,8 @@ VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The remainder of this paper presents current capabilities of the VERTEX-CFD package and provides a timeline for future work. + ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) From f3000f9298fabc9c886df2e208a807c13261673f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 6 Dec 2024 07:22:49 -0500 Subject: [PATCH 035/214] update documentation --- index.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index 935face..6d91f74 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,15 @@ -VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures, built upon the [Trilinos](https://trilinos.github.io/) numerical library. +# Summary: + +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. + + +# Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The remainder of this paper presents current capabilities of the VERTEX-CFD package and provides a timeline for future work. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with CPU and GPU architectures. + + +# Mathematics ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) From 108fbae041e571160372d2e175ac242ead16aa8d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 14 Dec 2024 07:44:21 -0500 Subject: [PATCH 036/214] update doc --- index.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index 6d91f74..e51a960 100644 --- a/index.md +++ b/index.md @@ -3,14 +3,17 @@ The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. -# Statement of need +# Statement of need and development plan The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with CPU and GPU architectures. - # Mathematics +# Conclusions + +# Acknolegements + ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) From 5d6cc35faa64c85211abb481087d3c9aafe99638 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 14 Dec 2024 08:04:39 -0500 Subject: [PATCH 037/214] update doc --- index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/index.md b/index.md index e51a960..d40c999 100644 --- a/index.md +++ b/index.md @@ -11,9 +11,15 @@ As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is # Mathematics # Conclusions +ERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. + +Future work includes addition of new RANS turbulent models, verification and validation of the MHD solver, implementation of large eddy simulation models, and deployment of VERTEX-CFD on Summit \cite{summit2018} for testing and optimization of the solver on NVIDIA GPUs. # Acknolegements +# Disclaimer +Notice: This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). + ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) ## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) From 79819d0a90aada92e17bd9fb114cda784da9a3aa Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 14 Dec 2024 08:13:57 -0500 Subject: [PATCH 038/214] update doc --- index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index d40c999..86452e8 100644 --- a/index.md +++ b/index.md @@ -11,14 +11,15 @@ As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is # Mathematics # Conclusions -ERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. +VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. -Future work includes addition of new RANS turbulent models, verification and validation of the MHD solver, implementation of large eddy simulation models, and deployment of VERTEX-CFD on Summit \cite{summit2018} for testing and optimization of the solver on NVIDIA GPUs. +Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. # Acknolegements +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. # Disclaimer -Notice: This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). ## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) From d8c93d63b3df0dd36d7011eb2bda8a03827d6beb Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 14 Dec 2024 08:27:05 -0500 Subject: [PATCH 039/214] update doc --- index.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index 86452e8..529e39e 100644 --- a/index.md +++ b/index.md @@ -3,10 +3,13 @@ The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. -# Statement of need and development plan +# Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. + +# Current capabilities +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction model, RANS turbulence models and LES turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. # Mathematics From fd605844ace76faec20ce898e8467d452024b87e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 14 Dec 2024 08:40:38 -0500 Subject: [PATCH 040/214] update doc --- index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.md b/index.md index 529e39e..b155cd4 100644 --- a/index.md +++ b/index.md @@ -7,9 +7,10 @@ The demand for high-performance computational fluid dynamics and multiphysics so The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. # Current capabilities -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction model, RANS turbulence models and LES turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). # Mathematics From cb503691bd96de3b9fa204672fe9a19fd46eac2f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 16:34:53 -0500 Subject: [PATCH 041/214] adding bibliography --- index.bib | 6 ++++++ index.md | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 index.bib diff --git a/index.bib b/index.bib new file mode 100644 index 0000000..8bf83a9 --- /dev/null +++ b/index.bib @@ -0,0 +1,6 @@ +@Manual{trilinos-website, +title = {{T}rilinos {P}roject {W}ebsite}, +author = {{T}rilinos {P}roject {T}eam}}, +year = {2020 (acccessed May 22, 2020)}, +url = {https://trilinos.github.io} +} diff --git a/index.md b/index.md index b155cd4..c8daa63 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,20 @@ +title: 'VERTEX-CFD: A multiphysics platform for fusion applications' +tags: + - C++ + - Trilinos + - Computational Fluid Dynamics + - Kokkos +authors: + - name: + given-names: Marc-Olivier + surname: Delchini + - name: + given-names: Kellis + surname: Kincaid + +date: 19 December 2024 +bibliography: index.bib + # Summary: The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. From f5d6f4bafe66ba5d81860b0315c2a93bbbb87146 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 16:40:13 -0500 Subject: [PATCH 042/214] add config gile --- index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/index.md b/index.md index c8daa63..b026312 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,4 @@ +--- title: 'VERTEX-CFD: A multiphysics platform for fusion applications' tags: - C++ From e5ea7d2434b62ffad099a74cb38fefd209da8532 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 16:48:55 -0500 Subject: [PATCH 043/214] add config gile --- index.md | 16 -------------- index.bib => paper.bib | 0 paper.md | 50 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 16 deletions(-) rename index.bib => paper.bib (100%) create mode 100644 paper.md diff --git a/index.md b/index.md index b026312..a83b933 100644 --- a/index.md +++ b/index.md @@ -1,20 +1,4 @@ --- -title: 'VERTEX-CFD: A multiphysics platform for fusion applications' -tags: - - C++ - - Trilinos - - Computational Fluid Dynamics - - Kokkos -authors: - - name: - given-names: Marc-Olivier - surname: Delchini - - name: - given-names: Kellis - surname: Kincaid - -date: 19 December 2024 -bibliography: index.bib # Summary: diff --git a/index.bib b/paper.bib similarity index 100% rename from index.bib rename to paper.bib diff --git a/paper.md b/paper.md new file mode 100644 index 0000000..bcfaccc --- /dev/null +++ b/paper.md @@ -0,0 +1,50 @@ +--- +title: 'VERTEX-CFD: A multiphysics platform for fusion applications' +tags: + - C++ + - Trilinos + - Computational Fluid Dynamics + - Kokkos +authors: + - name: + given-names: Marc-Olivier + surname: Delchini + - name: + given-names: Kellis + surname: Kincaid + +date: 19 December 2024 +bibliography: paper.bib + +# Summary: + +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. + + +# Statement of need + +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. + +# Current capabilities +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). + +# Mathematics + +# Conclusions +VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. + +Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. + +# Acknolegements +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. + +# Disclaimer +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). + +## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + +## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From 70693a097de39953175e31aa24e8f96aa404213e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 16:52:06 -0500 Subject: [PATCH 044/214] add config gile --- .github/workflows/draft-pdf.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/draft-pdf.yml diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml new file mode 100644 index 0000000..1d7dc44 --- /dev/null +++ b/.github/workflows/draft-pdf.yml @@ -0,0 +1,24 @@ +name: Draft PDF +on: [push] + +jobs: + paper: + runs-on: ubuntu-latest + name: Paper Draft + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build draft PDF + uses: openjournals/openjournals-draft-action@master + with: + journal: joss + # This should be the path to the paper within your repo. + paper-path: paper.md + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: paper + # This is the output path where Pandoc will write the compiled + # PDF. Note, this should be the same directory as the input + # paper.md + path: paper.pdf From 80a04180ffdd123b7c2b7856e12fea65d0e10703 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 17:03:25 -0500 Subject: [PATCH 045/214] add config gile --- paper.md | 35 +---------------------------------- paper.md.save | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 paper.md.save diff --git a/paper.md b/paper.md index bcfaccc..41269f5 100644 --- a/paper.md +++ b/paper.md @@ -5,7 +5,7 @@ tags: - Trilinos - Computational Fluid Dynamics - Kokkos -authors: +author: - name: given-names: Marc-Olivier surname: Delchini @@ -15,36 +15,3 @@ authors: date: 19 December 2024 bibliography: paper.bib - -# Summary: - -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. - - -# Statement of need - -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. - -# Current capabilities -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). - -# Mathematics - -# Conclusions -VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. - -Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. - -# Acknolegements -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. - -# Disclaimer -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). - -## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - -## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) diff --git a/paper.md.save b/paper.md.save new file mode 100644 index 0000000..bcfaccc --- /dev/null +++ b/paper.md.save @@ -0,0 +1,50 @@ +--- +title: 'VERTEX-CFD: A multiphysics platform for fusion applications' +tags: + - C++ + - Trilinos + - Computational Fluid Dynamics + - Kokkos +authors: + - name: + given-names: Marc-Olivier + surname: Delchini + - name: + given-names: Kellis + surname: Kincaid + +date: 19 December 2024 +bibliography: paper.bib + +# Summary: + +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. + + +# Statement of need + +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. + +# Current capabilities +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). + +# Mathematics + +# Conclusions +VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. + +Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. + +# Acknolegements +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. + +# Disclaimer +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). + +## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + +## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + +## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From cf4091f6e44750f39ba0fad19250ef7f1d011f00 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 17:06:34 -0500 Subject: [PATCH 046/214] add config gile --- paper.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/paper.md b/paper.md index 41269f5..8ef7a3d 100644 --- a/paper.md +++ b/paper.md @@ -1,17 +1,4 @@ --- title: 'VERTEX-CFD: A multiphysics platform for fusion applications' -tags: - - C++ - - Trilinos - - Computational Fluid Dynamics - - Kokkos -author: - - name: - given-names: Marc-Olivier - surname: Delchini - - name: - given-names: Kellis - surname: Kincaid - date: 19 December 2024 bibliography: paper.bib From d4b975f6ba0ec0a37ae12630289bfbc7a8589bdc Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 17:30:10 -0500 Subject: [PATCH 047/214] add config gile --- index.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/index.md b/index.md index a83b933..a5e48ad 100644 --- a/index.md +++ b/index.md @@ -8,27 +8,39 @@ The demand for high-performance computational fluid dynamics and multiphysics so # Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/). The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. + # Current capabilities + VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). + # Mathematics + # Conclusions + VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. -Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. +Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier for testing and optimization of the solver on AMD GPUs. + # Acknolegements + This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. + # Disclaimer + This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). -## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) -## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) +# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + + +# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) +# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From 57f2693cdeff3ef5e29075f316acf08fbe1a83c4 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 19 Dec 2024 17:45:43 -0500 Subject: [PATCH 048/214] add config gile --- index.md | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/index.md b/index.md index a5e48ad..6b6866d 100644 --- a/index.md +++ b/index.md @@ -2,7 +2,7 @@ # Summary: -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. # Statement of need @@ -17,14 +17,13 @@ The long term objectives of the VERTEX initiative is to faciliate the addiiton o VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). -# Mathematics +# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) -# Conclusions +# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) -VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. -Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier for testing and optimization of the solver on AMD GPUs. +# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) # Acknolegements @@ -34,13 +33,4 @@ This work was funded by the Laboratory Directed Research and Development (LDRD) # Disclaimer -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). - - -# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - - -# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - - -# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). From eeffc349d94f847c8776548239543c4ac01fb673 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:04:35 -0500 Subject: [PATCH 049/214] add config gile --- paper.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/paper.md b/paper.md index 8ef7a3d..4ff044b 100644 --- a/paper.md +++ b/paper.md @@ -1,4 +1,14 @@ --- title: 'VERTEX-CFD: A multiphysics platform for fusion applications' +tags: + - Computational Fluid Dynamics +authors: + - name: Marco + affiliation: "1" + corresponding: true +affiliations: +- name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory + index: 1 date: 19 December 2024 bibliography: paper.bib +--- From 3310a4de87e491a64ac34df648d95d9be58f4a78 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:08:25 -0500 Subject: [PATCH 050/214] add config gile --- .github/workflows/draft-pdf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml index 1d7dc44..682fe6e 100644 --- a/.github/workflows/draft-pdf.yml +++ b/.github/workflows/draft-pdf.yml @@ -15,7 +15,7 @@ jobs: # This should be the path to the paper within your repo. paper-path: paper.md - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: paper # This is the output path where Pandoc will write the compiled From 75c0ea3577aa6b862970e9b623013f70fde5b458 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:14:35 -0500 Subject: [PATCH 051/214] add config gile --- .github/workflows/draft-pdf.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml index 682fe6e..0df0791 100644 --- a/.github/workflows/draft-pdf.yml +++ b/.github/workflows/draft-pdf.yml @@ -1,6 +1,5 @@ name: Draft PDF on: [push] - jobs: paper: runs-on: ubuntu-latest From b95824072756dc32959f5ab78a98a360bd7922c0 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:16:04 -0500 Subject: [PATCH 052/214] add config gile --- paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper.md b/paper.md index 4ff044b..11ead1e 100644 --- a/paper.md +++ b/paper.md @@ -4,7 +4,7 @@ tags: - Computational Fluid Dynamics authors: - name: Marco - affiliation: "1" + affiliation: 1 corresponding: true affiliations: - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory From fdfa7ecebc187f5cc37985e71aa681bbe9cb488d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:19:07 -0500 Subject: [PATCH 053/214] add config gile --- paper.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paper.md b/paper.md index 11ead1e..dea1f90 100644 --- a/paper.md +++ b/paper.md @@ -7,8 +7,8 @@ authors: affiliation: 1 corresponding: true affiliations: -- name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory - index: 1 + - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory + index: 1 date: 19 December 2024 bibliography: paper.bib --- From 91355afceac6b7e9d2a9a79a5c1db1f354425d63 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:49:56 -0500 Subject: [PATCH 054/214] add config gile --- paper.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/paper.md b/paper.md index dea1f90..c5effa7 100644 --- a/paper.md +++ b/paper.md @@ -12,3 +12,7 @@ affiliations: date: 19 December 2024 bibliography: paper.bib --- + +# Summary: + +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. From 9ed426671a7d82583170ba6a89197064958dece0 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 09:55:56 -0500 Subject: [PATCH 055/214] add config gile --- paper.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/paper.md b/paper.md index c5effa7..041d3ec 100644 --- a/paper.md +++ b/paper.md @@ -16,3 +16,14 @@ bibliography: paper.bib # Summary: The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. + + +# Statement of need + +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. + + +# Current capabilities +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). From 333e7c0aa760701bf6972258a712e429220a4938 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 12:33:59 -0500 Subject: [PATCH 056/214] add config gile --- paper.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/paper.md b/paper.md index 041d3ec..6c6fe0d 100644 --- a/paper.md +++ b/paper.md @@ -26,4 +26,20 @@ The long term objectives of the VERTEX initiative is to faciliate the addiiton o # Current capabilities + VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). + + +# Conclusions + +VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. + + +# Acknolegements + +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. + + +# Disclaimer + +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). From b071c21b626da49f9b29c5c33aebd9a8ddc0d434 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 12:37:11 -0500 Subject: [PATCH 057/214] add config gile --- paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper.md b/paper.md index 6c6fe0d..5a4662f 100644 --- a/paper.md +++ b/paper.md @@ -21,7 +21,7 @@ The demand for high-performance computational fluid dynamics and multiphysics so # Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) @[trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. From c4eb896b4a32e9b12c86fad826aee3f4ab2f0d5d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 12:42:20 -0500 Subject: [PATCH 058/214] add config gile --- paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper.md b/paper.md index 5a4662f..25b3cb2 100644 --- a/paper.md +++ b/paper.md @@ -21,7 +21,7 @@ The demand for high-performance computational fluid dynamics and multiphysics so # Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) @[trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. From d8d4626d7adb5f7af5019882e1170d0431bf2db3 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 20 Dec 2024 12:45:16 -0500 Subject: [PATCH 059/214] add config gile --- paper.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paper.md b/paper.md index 25b3cb2..9f83045 100644 --- a/paper.md +++ b/paper.md @@ -3,9 +3,11 @@ title: 'VERTEX-CFD: A multiphysics platform for fusion applications' tags: - Computational Fluid Dynamics authors: - - name: Marco + - name: Marco Delchini affiliation: 1 corresponding: true + - name: Kellis Kincaid + affiliation: 1 affiliations: - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory index: 1 From db3d9023ed024a8b04ec1bb18899ac7993403202 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 12:19:07 -0500 Subject: [PATCH 060/214] Update paper.md --- paper.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/paper.md b/paper.md index 9f83045..e80dff1 100644 --- a/paper.md +++ b/paper.md @@ -2,6 +2,8 @@ title: 'VERTEX-CFD: A multiphysics platform for fusion applications' tags: - Computational Fluid Dynamics + - CPU and GPU + - Finite Element Method authors: - name: Marco Delchini affiliation: 1 @@ -17,20 +19,19 @@ bibliography: paper.bib # Summary: -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. - +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows, turbulent flows, and MHD flows. # Statement of need -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics. As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. -# Current capabilities +# Current capabilities and development worflow VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). +The long term objectives of the VERTEX initiative is to faciliate the additon of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. # Conclusions From 28a7ccfe93ec1ca5b5b7297b8d836a6ba9b2033c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 12:36:30 -0500 Subject: [PATCH 061/214] Update paper.md --- paper.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/paper.md b/paper.md index e80dff1..0880bff 100644 --- a/paper.md +++ b/paper.md @@ -10,9 +10,19 @@ authors: corresponding: true - name: Kellis Kincaid affiliation: 1 + - name: Furkan Oz + affiliation: 1 + - name: Jason DeGraw + affiliation: 2 + - name: Kalyan Gottiparthi + affiliation: 3 affiliations: - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory index: 1 + - name: Building and Transportation Division, Oak Ridge National Laboratory + index: 2 + - name: National Center for Computation Science Division, Oak Ridge National Laboratory + index: 3 date: 19 December 2024 bibliography: paper.bib --- @@ -35,7 +45,9 @@ The long term objectives of the VERTEX initiative is to faciliate the additon of # Conclusions -VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. +VERTEX-CFD is an open-source CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and MHD equation. Reynolds Averaged Navier-Stokes (RANS) turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. + +Future development will focus on implementing wall functions for RANS models, and the addition of conjugate heat transfer capabilities. # Acknolegements From bd50818879fb97b0c57f51419fdbfc3045a0f53e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 12:37:02 -0500 Subject: [PATCH 062/214] Update paper.bib --- paper.bib | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/paper.bib b/paper.bib index 8bf83a9..435b145 100644 --- a/paper.bib +++ b/paper.bib @@ -4,3 +4,18 @@ @Manual{trilinos-website year = {2020 (acccessed May 22, 2020)}, url = {https://trilinos.github.io} } + +@article{Clausen2013, + title = {Entropically damped form of artificial compressibility for explicit simulation of incompressible flow}, + author = {Clausen, Jonathan R.}, + journal = {Phys. Rev. E}, + volume = {87}, + issue = {1}, + pages = {013309}, + numpages = {12}, + year = {2013}, + month = {Jan}, + publisher = {American Physical Society}, + doi = {10.1103/PhysRevE.87.013309}, + url = {https://link.aps.org/doi/10.1103/PhysRevE.87.013309} +} From 9a214fc56f9d5e2cc0259ddb6656064cddefbf91 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 12:39:25 -0500 Subject: [PATCH 063/214] Update paper.md --- paper.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paper.md b/paper.md index 0880bff..f73a91b 100644 --- a/paper.md +++ b/paper.md @@ -29,7 +29,7 @@ bibliography: paper.bib # Summary: -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows, turbulent flows, and MHD flows. +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems taken from the published literature. # Statement of need @@ -39,7 +39,7 @@ As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is # Current capabilities and development worflow -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). The long term objectives of the VERTEX initiative is to faciliate the additon of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. From 7f075cb00e3729af3c6201d7621391fa131d1bdd Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 13:01:53 -0500 Subject: [PATCH 064/214] update paper.md --- paper.bib | 24 ++++++++++++++++++++++++ paper.md | 22 +++++++++++++++------- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/paper.bib b/paper.bib index 435b145..c6569c4 100644 --- a/paper.bib +++ b/paper.bib @@ -19,3 +19,27 @@ @article{Clausen2013 doi = {10.1103/PhysRevE.87.013309}, url = {https://link.aps.org/doi/10.1103/PhysRevE.87.013309} } + +@misc{olcf-web, + title = {OLCF}, + note = {https://www.olcf.ornl.gov/}, + url = {https://www.olcf.ornl.gov/}, + year = {2024} +} + +@article{nicoud:hal-00910373, + TITLE = {{Subgrid-scale stress modelling based on the square of the velocity gradient tensor}}, + AUTHOR = {Nicoud, Franck and Ducros, Fr{\'e}d{\'e}ric}, + URL = {https://hal.science/hal-00910373}, + JOURNAL = {{Flow, Turbulence and Combustion}}, + PUBLISHER = {{Springer Verlag}}, + VOLUME = {62}, + NUMBER = {3}, + PAGES = {183-200}, + YEAR = {1999}, + DOI = {10.1023/A:1009995426001}, + KEYWORDS = {large eddy simulations ; wall-bounded flow ; unstructured mesh ; transition ; LARGE-EDDY SIMULATION ; NUMERICAL-SIMULATION ; TURBULENCE ; FLOW}, + PDF = {https://hal.science/hal-00910373v1/file/paper.pdf}, + HAL_ID = {hal-00910373}, + HAL_VERSION = {v1}, +} \ No newline at end of file diff --git a/paper.md b/paper.md index f73a91b..c936f28 100644 --- a/paper.md +++ b/paper.md @@ -16,6 +16,10 @@ authors: affiliation: 2 - name: Kalyan Gottiparthi affiliation: 3 + - name: Ryan Glasby + affiliation: 4 + - name: Franklin Stuart + affiliation: 5 affiliations: - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory index: 1 @@ -23,6 +27,10 @@ affiliations: index: 2 - name: National Center for Computation Science Division, Oak Ridge National Laboratory index: 3 + - name: Computational Science and Engineering Division, Oak Ridge National Laboratory + index: 4 + - name: Enrichment Science and Engineering Division, Oak Ridge National Laboratory + index: 5 date: 19 December 2024 bibliography: paper.bib --- @@ -33,26 +41,26 @@ The demand for high-performance computational fluid dynamics and multiphysics so # Statement of need -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics. +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -# Current capabilities and development worflow +# Current capabilities and development workflow -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). +VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). -The long term objectives of the VERTEX initiative is to faciliate the additon of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. +The long term objectives of the VERTEX initiative is to facilitate the addition of new physical models by relying on a plug-and-play architecture, and also guarantee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and additions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. # Conclusions -VERTEX-CFD is an open-source CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and MHD equation. Reynolds Averaged Navier-Stokes (RANS) turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. +VERTEX-CFD is an open-source CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and MHD equation. Reynolds Averaged Navier-Stokes (RANS) turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. VERTEX-CFD solver was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit [@olcf-web]) architectures. Future development will focus on implementing wall functions for RANS models, and the addition of conjugate heat transfer capabilities. -# Acknolegements +# Acknowledgements -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) program. # Disclaimer From 2637b3a5f470ec07b137a7225394bc163c3f4f5a Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 13:14:22 -0500 Subject: [PATCH 065/214] update --- paper.bib | 12 +++++++++++- paper.md | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/paper.bib b/paper.bib index c6569c4..540bdaa 100644 --- a/paper.bib +++ b/paper.bib @@ -42,4 +42,14 @@ @article{nicoud:hal-00910373 PDF = {https://hal.science/hal-00910373v1/file/paper.pdf}, HAL_ID = {hal-00910373}, HAL_VERSION = {v1}, -} \ No newline at end of file +} + +@article{kokkos, + author={Trott, Christian R. and Lebrun-Grandié, Damien and Arndt, Daniel and Ciesko, Jan and Dang, Vinh and Ellingwood, Nathan and Gayatri, Rahulkumar and Harvey, Evan and Hollman, Daisy S. and Ibanez, Dan and Liber, Nevin and Madsen, Jonathan and Miles, Jeff and Poliakoff, David and Powell, Amy and Rajamanickam, Sivasankaran and Simberg, Mikael and Sunderland, Dan and Turcksin, Bruno and Wilke, Jeremiah}, + journal={IEEE Transactions on Parallel and Distributed Systems}, + title={Kokkos 3: Programming Model Extensions for the Exascale Era}, + year={2022}, + volume={33}, + number={4}, + pages={805-817}, + doi={10.1109/TPDS.2021.3097283}} \ No newline at end of file diff --git a/paper.md b/paper.md index c936f28..9047fec 100644 --- a/paper.md +++ b/paper.md @@ -42,7 +42,7 @@ The demand for high-performance computational fluid dynamics and multiphysics so # Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos [@kokkos] programming language to ensure compatibility with various CPU and GPU architectures. # Current capabilities and development workflow From 8ab06aaa6d6562eb520f2b8181a5b713a3dd78b2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 20:32:28 -0500 Subject: [PATCH 066/214] update --- paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper.md b/paper.md index 9047fec..bd3ab8b 100644 --- a/paper.md +++ b/paper.md @@ -55,7 +55,7 @@ The long term objectives of the VERTEX initiative is to facilitate the addition VERTEX-CFD is an open-source CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and MHD equation. Reynolds Averaged Navier-Stokes (RANS) turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. VERTEX-CFD solver was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit [@olcf-web]) architectures. -Future development will focus on implementing wall functions for RANS models, and the addition of conjugate heat transfer capabilities. +Future development will focus on implementing wall functions for RANS models, and adding conjugate heat transfer capabilities. # Acknowledgements From f1623d9f9ddee1c37c4ebb888860fad0b067f87b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 20:44:15 -0500 Subject: [PATCH 067/214] update --- index.md | 47 ++++++++++++++++------------------------------- index.md.save | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 31 deletions(-) create mode 100644 index.md.save diff --git a/index.md b/index.md index 6b6866d..7338d36 100644 --- a/index.md +++ b/index.md @@ -1,36 +1,21 @@ --- +layout: default +title: Home +nav_order: 1 +permalink: / +--- -# Summary: - -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. - - -# Statement of need - -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/). The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. - - -# Current capabilities - -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). - - -# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - - -# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - - -# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) - - -# Acknolegements - -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. +An open-source CFD code for additive manufacturing built on OpenFOAM. +--- -# Disclaimer +## Contributing +We encourage you to contribute to AdditiveFOAM! Please check the guidelines on how to do so. -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). +#### Contributors +- [John Coleman](https://www.ornl.gov/staff-profile/john-s-coleman) +- [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) +- [Gerry L. Knapp](https://www.ornl.gov/staff-profile/gerald-l-knapp) +- [Benjamin Stump](https://www.ornl.gov/staff-profile/benjamin-c-stump) +- [Alex Plotkowski](https://www.ornl.gov/staff-profile/alex-j-plotkowski) +- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) \ No newline at end of file diff --git a/index.md.save b/index.md.save new file mode 100644 index 0000000..6b6866d --- /dev/null +++ b/index.md.save @@ -0,0 +1,36 @@ +--- + +# Summary: + +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. + + +# Statement of need + +The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/). The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. +The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. + + +# Current capabilities + +VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). + + +# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) + + +# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) + + +# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) + + +# Acknolegements + +This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. + + +# Disclaimer + +This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). From f98f36b9b9cb58f93e8db88e47f653cba45acf33 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 20:51:05 -0500 Subject: [PATCH 068/214] update --- index.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/index.md b/index.md index 7338d36..9770729 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,8 @@ nav_order: 1 permalink: / --- -An open-source CFD code for additive manufacturing built on OpenFOAM. +An open-source CFD code for multi-physics modeling and simulation with a focus on fusion application. +{: .fs-6 .fw-300 } --- @@ -13,9 +14,9 @@ An open-source CFD code for additive manufacturing built on OpenFOAM. We encourage you to contribute to AdditiveFOAM! Please check the guidelines on how to do so. #### Contributors -- [John Coleman](https://www.ornl.gov/staff-profile/john-s-coleman) +- [Marco Delchini](https://www.ornl.gov/staff-profile/marc-olivier-delchini) - [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) -- [Gerry L. Knapp](https://www.ornl.gov/staff-profile/gerald-l-knapp) -- [Benjamin Stump](https://www.ornl.gov/staff-profile/benjamin-c-stump) -- [Alex Plotkowski](https://www.ornl.gov/staff-profile/alex-j-plotkowski) -- [Sam T. Reeve](https://www.ornl.gov/staff-profile/samuel-t-reeve) \ No newline at end of file +- [Furkan Oz](https://www.ornl.gov/staff-profile/furkan-oz) + + +## Citing \ No newline at end of file From 53d20aafc351344cea0a838d1c54b80195d36537 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 20:56:02 -0500 Subject: [PATCH 069/214] update --- index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.md b/index.md index 9770729..3c6dda5 100644 --- a/index.md +++ b/index.md @@ -17,6 +17,8 @@ We encourage you to contribute to AdditiveFOAM! Please check the guidelines on h - [Marco Delchini](https://www.ornl.gov/staff-profile/marc-olivier-delchini) - [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) - [Furkan Oz](https://www.ornl.gov/staff-profile/furkan-oz) - +- [Kalyan Gottiparthi](https://www.ornl.gov/staff-profile/kalyan-c-gottiparthi) +- [Jason Degraw](https://www.ornl.gov/staff-profile/jason-w-degraw) +- []() ## Citing \ No newline at end of file From 8adedb28a5e03e770f06c099abade71836d691d6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:00:57 -0500 Subject: [PATCH 070/214] update --- index.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index 3c6dda5..9834540 100644 --- a/index.md +++ b/index.md @@ -5,13 +5,16 @@ nav_order: 1 permalink: / --- -An open-source CFD code for multi-physics modeling and simulation with a focus on fusion application. +An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } +[User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } +[View it on GitHub][AdditiveFOAM repo]{: .btn .fs-5 .mb-4 .mb-md-0 } + --- ## Contributing -We encourage you to contribute to AdditiveFOAM! Please check the guidelines on how to do so. +We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how to do so. #### Contributors - [Marco Delchini](https://www.ornl.gov/staff-profile/marc-olivier-delchini) @@ -19,6 +22,6 @@ We encourage you to contribute to AdditiveFOAM! Please check the guidelines on h - [Furkan Oz](https://www.ornl.gov/staff-profile/furkan-oz) - [Kalyan Gottiparthi](https://www.ornl.gov/staff-profile/kalyan-c-gottiparthi) - [Jason Degraw](https://www.ornl.gov/staff-profile/jason-w-degraw) -- []() +- [Filipe Leite Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) ## Citing \ No newline at end of file From d5f3a0ee8cb847b55e9c6bb3bc3ca79439373562 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:03:52 -0500 Subject: [PATCH 071/214] update --- index.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/index.md b/index.md index 9834540..4dcd448 100644 --- a/index.md +++ b/index.md @@ -9,6 +9,7 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o {: .fs-6 .fw-300 } [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } + [View it on GitHub][AdditiveFOAM repo]{: .btn .fs-5 .mb-4 .mb-md-0 } --- @@ -24,4 +25,17 @@ We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how - [Jason Degraw](https://www.ornl.gov/staff-profile/jason-w-degraw) - [Filipe Leite Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) -## Citing \ No newline at end of file +## Citing +If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](DOI_NUMBER) of the version you used as a software citation: +```bibtex +@software{vertex-cod, + author = {AUTHORS}, + title = {VERTEX-CFD: Release 1.0}, + month = jun, + year = 2024, + publisher = {Zenodo}, + version = {1.0.0}, + doi = {DOI_NUMBER}, + url = {DOI_URL} +} +``` \ No newline at end of file From 933ea973cd3dccbe888016d3a87516a9e68900e4 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:12:47 -0500 Subject: [PATCH 072/214] update --- docs/index.md | 13 +++++++++++++ docs/installation.md | 0 index.md | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 docs/index.md create mode 100644 docs/installation.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a80d015 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,13 @@ +--- +layout: page +title: VERTEX-CFD v1.0 User Guide +nav_order: 2 +has_children: true +--- + +# VERTEX-CFD v1.0 User Guide +VERTEX-CFD is a free, open source multi physics software for simulations of fusion applications released by Oak Ridge National Laboratory. +--- + +{: .custom } +The User Guide v1.0 is currently under development and may be incomplete. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..e69de29 diff --git a/index.md b/index.md index 4dcd448..3e11f28 100644 --- a/index.md +++ b/index.md @@ -28,7 +28,7 @@ We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how ## Citing If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](DOI_NUMBER) of the version you used as a software citation: ```bibtex -@software{vertex-cod, +@software{vertex-cfd, author = {AUTHORS}, title = {VERTEX-CFD: Release 1.0}, month = jun, From 2db9827b3f9d532622c4deed4ff430108f3d070b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:30:46 -0500 Subject: [PATCH 073/214] update --- docs/theory.md | 0 docs/usage.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/theory.md create mode 100644 docs/usage.md diff --git a/docs/theory.md b/docs/theory.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..e69de29 From 978c56a8db9f5818b9cf79f87b89116da519eb2c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:35:16 -0500 Subject: [PATCH 074/214] update --- .github/workflows/draft-pdf.yml | 4 ++-- paper.bib => paper/paper.bib | 0 paper.md => paper/paper.md | 0 paper.md.save => paper/paper.md.save | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename paper.bib => paper/paper.bib (100%) rename paper.md => paper/paper.md (100%) rename paper.md.save => paper/paper.md.save (100%) diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml index 0df0791..0fc44ec 100644 --- a/.github/workflows/draft-pdf.yml +++ b/.github/workflows/draft-pdf.yml @@ -12,7 +12,7 @@ jobs: with: journal: joss # This should be the path to the paper within your repo. - paper-path: paper.md + paper-path: paper/paper.md - name: Upload uses: actions/upload-artifact@v3 with: @@ -20,4 +20,4 @@ jobs: # This is the output path where Pandoc will write the compiled # PDF. Note, this should be the same directory as the input # paper.md - path: paper.pdf + path: paper/paper.pdf diff --git a/paper.bib b/paper/paper.bib similarity index 100% rename from paper.bib rename to paper/paper.bib diff --git a/paper.md b/paper/paper.md similarity index 100% rename from paper.md rename to paper/paper.md diff --git a/paper.md.save b/paper/paper.md.save similarity index 100% rename from paper.md.save rename to paper/paper.md.save From 1cb4fc0168b0a30fbc6ddcb2c230fc7705c0bc0f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 28 Dec 2024 21:36:55 -0500 Subject: [PATCH 075/214] update --- index.md.save | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 index.md.save diff --git a/index.md.save b/index.md.save deleted file mode 100644 index 6b6866d..0000000 --- a/index.md.save +++ /dev/null @@ -1,36 +0,0 @@ ---- - -# Summary: - -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. - - -# Statement of need - -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/). The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. - - -# Current capabilities - -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). - - -# [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - - -# [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - - -# [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) - - -# Acknolegements - -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. - - -# Disclaimer - -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). From 9e548ebf556008909b3b1bb1f1c7537babe39b48 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sun, 29 Dec 2024 09:40:41 -0500 Subject: [PATCH 076/214] update --- .DS_Store | Bin 0 -> 6148 bytes _config.yml | 2 +- ...go.png => vertex_cfd_heated_flow_logo.png} | Bin paper/paper.bib | 58 +++++++++++++++++- paper/paper.md | 6 +- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 .DS_Store rename docs/figures/{logo.png => vertex_cfd_heated_flow_logo.png} (100%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..59d0962b23d2c99d784482065ea9f24929d2fd47 GIT binary patch literal 6148 zcmeHKI|>3Z5S{S@f{mqRuHX%V=n3`$3W|-W2wHFDxjdS0KFzY&X`#G<$x9~l67q_j z9TCyxZMP7aiO2+QC=VO@X8Yzn8)QU*aGY_yH`mkgd^+@U-vx|2mWyoR`wriBXjFg- zPys4H1*pKM703!ZnST0Uo<{|!z|SjS--iM>tch))e>yOD3jiD;?1s7b62M{sU`=cT z5rJt?fkD-5F*N9im&~h)ZD7zvv-!}xS+he?za8fnPZzC$9H{^m=qfOb<;?2;8vdsL z?~=Hp0#x9y6wuMCSuOFTtgXGrS*mDrVEV + +# Flow around a circle +@article{tritton_1959, title={Experiments on the flow past a circular cylinder at low Reynolds numbers}, volume={6}, DOI={10.1017/S0022112059000829}, number={4}, journal={Journal of Fluid Mechanics}, publisher={Cambridge University Press}, author={Tritton, D. J.}, year={1959}, pages={547–567}} + +# Flow around a blunt +@article{10.1115/1.3240731, + author = {Lane, J. C. and Loehrke, R. I.}, + title = "{Leading Edge Separation From a Blunt Plate at Low Reynolds Number}", + journal = {Journal of Fluids Engineering}, + volume = {102}, + number = {4}, + pages = {494-496}, + year = {1980}, + month = {12}, + abstract = "{The flow over a blunt plate aligned parallel to the stream was visualized using dye tracers. A leading edge separation bubble was observed to form at a Reynolds number based on plate thickness of 100. The steady, laminar separation bubble on a long plate, L/t ≥ 8, grows in size with increasing Reynolds number reaching a maximum streamwise length at Ret = 325. The separated shear layer becomes unsteady and the bubble shrinks in size with further increases in Reynolds number. The leading and trailing edge separation zones on short plates, L/t ≤ 4, may combine to form a large recirculation pocket.}", + issn = {0098-2202}, + doi = {10.1115/1.3240731}, + url = {https://doi.org/10.1115/1.3240731}, + eprint = {https://asmedigitalcollection.asme.org/fluidsengineering/article-pdf/102/4/494/5531330/494\_1.pdf}, +} \ No newline at end of file diff --git a/paper/paper.md b/paper/paper.md index bd3ab8b..f797398 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -37,17 +37,17 @@ bibliography: paper.bib # Summary: -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems taken from the published literature. +The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to effort in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on CPU and GPU computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems taken from the published literature. # Statement of need The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos [@kokkos] programming language to ensure compatibility with various CPU and GPU architectures. +As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale and to be compatible with various CPU and GPU architectures on HPC platforms by leveraging Kokkos [@kokkos] programming language. # Current capabilities and development workflow -VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). +VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against benchmark problems taken from the published literature: isothermal flows [@Taylor-green-vortex, @10.1115/1.3240731, @PhysRevE.87.013309], heated flows [@Kuehn_Goldstein_1976, @tritton_1959], transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). The long term objectives of the VERTEX initiative is to facilitate the addition of new physical models by relying on a plug-and-play architecture, and also guarantee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and additions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. From c0fd8d84ee6c777b8aab68da8f709c3c17bcd959 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sun, 29 Dec 2024 09:49:27 -0500 Subject: [PATCH 077/214] update --- paper/paper.bib | 37 +++++++++++++++++++++---------------- paper/paper.md | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/paper/paper.bib b/paper/paper.bib index 2bad219..6122053 100644 --- a/paper/paper.bib +++ b/paper/paper.bib @@ -70,22 +70,6 @@ @article{Taylor-green-vortex year = {1937} } -# Double shear layer -@article{PhysRevE.87.013309, - title = {Entropically damped form of artificial compressibility for explicit simulation of incompressible flow}, - author = {Clausen, Jonathan R.}, - journal = {Phys. Rev. E}, - volume = {87}, - issue = {1}, - pages = {013309}, - numpages = {12}, - year = {2013}, - month = {Jan}, - publisher = {American Physical Society}, - doi = {10.1103/PhysRevE.87.013309}, - url = {https://link.aps.org/doi/10.1103/PhysRevE.87.013309} -} - # Validation # Natural convection @article{Kuehn_Goldstein_1976, title={An experimental and theoretical study of natural convection in the annulus between horizontal concentric cylinders}, volume={74}, DOI={10.1017/S0022112076002012}, number={4}, journal={Journal of Fluid Mechanics}, author={Kuehn, T. H. and Goldstein, R. J.}, year={1976}, pages={695–719}}

@@ -108,4 +92,25 @@ @article{10.1115/1.3240731 doi = {10.1115/1.3240731}, url = {https://doi.org/10.1115/1.3240731}, eprint = {https://asmedigitalcollection.asme.org/fluidsengineering/article-pdf/102/4/494/5531330/494\_1.pdf}, +} + +@article{SMOLENTSEV201565, +title = {An approach to verification and validation of MHD codes for fusion applications}, +journal = {Fusion Engineering and Design}, +volume = {100}, +pages = {65-72}, +year = {2015}, +issn = {0920-3796}, +doi = {https://doi.org/10.1016/j.fusengdes.2014.04.049}, +url = {https://www.sciencedirect.com/science/article/pii/S0920379614003263}, +author = {S. Smolentsev and S. Badia and R. Bhattacharyay and L. Bühler and L. Chen and Q. Huang and H.-G. Jin and D. Krasnov and D.-W. Lee and E. Mas {de les Valls} and C. Mistrangelo and R. Munipalli and M.-J. Ni and D. Pashkevich and A. Patel and G. Pulugundla and P. Satyamurthy and A. Snegirev and V. Sviridov and P. Swain and T. Zhou and O. Zikanov}, +keywords = {Blanket, Liquid metal magnetohydrodynamics, Computer code}, +abstract = {We propose a new activity on verification and validation (V&V) of MHD codes presently employed by the fusion community as a predictive capability tool for liquid metal cooling applications, such as liquid metal blankets. The important steps in the development of MHD codes starting from the 1970s are outlined first and then basic MHD codes, which are currently in use by designers of liquid breeder blankets, are reviewed. A benchmark database of five problems has been proposed to cover a wide range of MHD flows from laminar fully developed to turbulent flows, which are of interest for fusion applications: (A) 2D fully developed laminar steady MHD flow, (B) 3D laminar, steady developing MHD flow in a non-uniform magnetic field, (C) quasi-two-dimensional MHD turbulent flow, (D) 3D turbulent MHD flow, and (E) MHD flow with heat transfer (buoyant convection). Finally, we introduce important details of the proposed activities, such as basic V&V rules and schedule. The main goal of the present paper is to help in establishing an efficient V&V framework and to initiate benchmarking among interested parties. The comparison results computed by the codes against analytical solutions and trusted experimental and numerical data as well as code-to-code comparisons will be presented and analyzed in companion paper/papers.} +} + +@misc{nasa-web, + title = {Turbulence Modeling Resource}, + note = {Langley Research Center}, + url = {https://turbmodels.larc.nasa.gov/}, + year = {2024} } \ No newline at end of file diff --git a/paper/paper.md b/paper/paper.md index f797398..9697c32 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -47,7 +47,7 @@ As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is # Current capabilities and development workflow -VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against benchmark problems taken from the published literature: isothermal flows [@Taylor-green-vortex, @10.1115/1.3240731, @PhysRevE.87.013309], heated flows [@Kuehn_Goldstein_1976, @tritton_1959], transient and steady-state cases, turbulent cases, and MHD flows. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). +VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against benchmark problems taken from the published literature: isothermal flows [@Taylor-green-vortex, @10.1115/1.3240731; @Clausen2013], heated flows [@Kuehn_Goldstein_1976; @tritton_1959], transient and steady-state cases, turbulent cases [@nicoud:hal-00910373; @nasa-web], and MHD flows [@SMOLENTSEV201565]. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). The long term objectives of the VERTEX initiative is to facilitate the addition of new physical models by relying on a plug-and-play architecture, and also guarantee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and additions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. From d6d18b2df0607787c75242cd7cf6ed818e8999aa Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sun, 29 Dec 2024 09:56:10 -0500 Subject: [PATCH 078/214] update --- docs/installation.md | 12 ++++++++++++ docs/theory.md | 18 ++++++++++++++++++ docs/usage.md | 14 ++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index e69de29..a57f8f6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -0,0 +1,12 @@ +--- +parent: VERTEX-CFD v1.0 User Guide +nav_order: 1 +--- + +# Installation + +--- + +## CPU installation + +## GPU installation diff --git a/docs/theory.md b/docs/theory.md index e69de29..4413572 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -0,0 +1,18 @@ +--- +parent: VERTEX-CFD v1.0 User Guide +title: Theory +nav_order: 2 +usemathjax: true +--- + +# Theory + +--- + +## The physics + +## The discretized equations + +## Boundary conditions + +## Initial conditions diff --git a/docs/usage.md b/docs/usage.md index e69de29..e8b5061 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -0,0 +1,14 @@ +--- +parent: VERTEX-CFD v1.0 User Guide +title: Usage +nav_order: 3 +usemathjax: true +--- + +# Usage + +--- + +## Running a simulation + +## Visualizing numerical results From 01165627e60b59c3066266bf3776b6c141678d4f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sun, 29 Dec 2024 09:58:51 -0500 Subject: [PATCH 079/214] update --- paper/paper.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/paper/paper.md b/paper/paper.md index 9697c32..6d74b3e 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -16,8 +16,12 @@ authors: affiliation: 2 - name: Kalyan Gottiparthi affiliation: 3 + - name: Doug Stefanski + affiliation: 4 - name: Ryan Glasby affiliation: 4 + - name: Stuart Slattery + affiliation: 4 - name: Franklin Stuart affiliation: 5 affiliations: From 977f0757983f8a4d71a1ab81d6bbe3aa34799ab0 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Wed, 8 Jan 2025 20:05:26 -0500 Subject: [PATCH 080/214] Updated usage.md --- docs/usage.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index e8b5061..35a97a2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -7,8 +7,96 @@ usemathjax: true # Usage ---- +Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, there are two executables, the first one is located in `BUILD_PATH/src/vertexcfd` and the second one is located in `INSTALL_PATH/bin/vertexcfd`. Both of the executables should work. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. ## Running a simulation +In order to run a simulation in serial, vertexcfd can be called directly as: + +``` +BUILD_PATH/src/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +``` +To run in parallel, `mpirun` is required. An example script for the SLURM scheduler is below: +``` +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node=32 +#SBATCH --time=1:00:00 +#SBATCH -o output.log +#SBATCH -e error.log + +source PATH_TO_ENVIRONMENT_SCRIPT + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +mpirun BUILD_PATH/src/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +``` +Once the simulation starts, the example output should look like: +``` +============================================================================ +Time Integration Begin +Thu Mar 31 21:46:09 2022 + + Stepper = Backward Euler + Simulation Time Range [0, 0.2] +---------------------------------------------------------------------------- + +Time Step = 1; Order = 1 +CFL = 1.000e+00; dt = 6.186e-03; Time = 0.00000e+00 + | Nonlinear | F 2-Norm | # Linear | R 2-Norm | + 0 6.65e-02 + 1 2.03e-02 1 6.45e-16 + 2 4.66e-04 1 4.25e-16 + 3 5.40e-07 1 5.36e-16 + 4 3.19e-13 1 6.58e-16 +Time step time to completion (s): 5.97e+00 + +Time Step = 2; Order = 1 +CFL = 1.000e+00; dt = 5.644e-03; Time = 6.18583e-03 + | Nonlinear | F 2-Norm | # Linear | R 2-Norm | + 0 5.73e-02 + 1 5.72e-03 1 4.68e-16 + 2 5.40e-05 1 4.01e-16 + 3 3.47e-09 1 7.18e-16 +Time step time to completion (s): 8.96e+00 + +Time Step = 3; Order = 1 +CFL = 1.000e+00; dt = 5.385e-03; Time = 1.18299e-02 + | Nonlinear | F 2-Norm | # Linear | R 2-Norm | + 0 5.37e-02 + 1 4.14e-03 1 4.17e-16 + 2 3.35e-05 1 3.47e-16 + 3 2.01e-09 1 5.54e-16 +Time step time to completion (s): 7.03e-01 +... ... +... ... +... ... +... ... +Time Step = 40; Order = 1 +CFL = 1.000e+00; dt = 4.853e-03; Time = 1.93731e-01 + | Nonlinear | F 2-Norm | # Linear | R 2-Norm | + 0 3.57e-02 + 1 1.15e-03 1 3.89e-16 + 2 1.68e-06 1 3.90e-16 + 3 1.66e-12 1 3.20e-16 +Time step time to completion (s): 1.73e+00 + 41 * (dt = 4.852e-03, new = 1.417e-03) Adjusting dt to hit final time. + +Time Step = 41; Order = 1 +CFL = 1.000e+00; dt = 1.417e-03; Time = 1.98583e-01 + | Nonlinear | F 2-Norm | # Linear | R 2-Norm | + 0 3.56e-02 + 1 3.11e-04 1 2.95e-16 + 2 3.67e-08 1 2.73e-16 + 3 4.40e-16 1 2.86e-16 +Time step time to completion (s): 6.91e+00 -## Visualizing numerical results +---------------------------------------------------------------------------- +Total runtime = 1.45e+02 sec + = 2.42e+00 min + = 0.04 hr +Thu Mar 31 21:51:51 2022 +Time integration complete. +============================================================================ +``` +Once the simulation is completed. The results should be ready for visualization. For the visualization, we suggest ParaView. However, any visualization software that supports Exodus format should work. From fd5ee5811bfb7b59e568bfa31a885acb26608b96 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Wed, 8 Jan 2025 23:01:30 -0500 Subject: [PATCH 081/214] Updated installation.md --- docs/installation.md | 61 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index a57f8f6..1799846 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,9 +4,66 @@ nav_order: 1 --- # Installation - ---- +VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, please refer to GPU installation. Otherwise, check CPU installation below. ## CPU installation +For the CPU installation, first of all, make sure you have `TRILINOS_HOME` variable defined in your environment. +``` +TRILINOS_HOME=PATH_TO_TRILINOS/TRILINOS_VERSION +``` +Once the `TRILINOS_HOME` is set, load the dependencies: +``` +module load intel +module load tbb +module load compiler-rt/ +module load mkl +module load python +``` +Once the environment is ready, configuration file can be run as: +``` +./vertexcfd-env +``` +The content of the `vertexcfd-env` file is: +``` +#!/bin/sh + +SOURCE=PATH_TO/vertex-cfd +INSTALL=INSTALLATION_PATH +Trilinos_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION +BUILD="Release" + +rm -rf CMake* +rm -rf .ninja* +rm DartConfiguration.tcl +rm CTestTestfile.cmake +rm build.ninja +rm VertexCFDConfig.cmake +rm -rf Testing + +cmake \ + -G Ninja \ + -D CMAKE_BUILD_TYPE="$BUILD" \ + -D CMAKE_INSTALL_PREFIX="$INSTALL" \ + -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ + -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ + -D VertexCFD_ENABLE_TESTING=ON \ + -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ + -D Trilinos_ROOT="$Trilinos_ROOT" \ + \ + ${SOURCE} +``` +Once the configuration is completed, compilation can be initiated with following command. +``` +ninja +``` +By default, `ninja` command will use the available cores, this can be limitted as: +``` +ninja -j4 +``` +You can replace the number 4 with the number of cores that you prefer. Once the compilation is done, VERTEX-CFD can be installed by using: +``` +ninja install +``` +Once installed, VERTEX-CFD is ready to run. ## GPU installation From cfe8ebca6307f70b44da8fcd9a92b2c43ade40de Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 21 Jan 2025 10:54:39 -0500 Subject: [PATCH 082/214] installation and usage files are updated based on the comments. --- docs/installation.md | 54 ++++++++++++++++++++++++++++++++++++++++---- docs/usage.md | 10 ++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 1799846..d1cb157 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -31,17 +31,16 @@ SOURCE=PATH_TO/vertex-cfd INSTALL=INSTALLATION_PATH Trilinos_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION BUILD="Release" +BUILD_SYSTEM=Ninja rm -rf CMake* -rm -rf .ninja* rm DartConfiguration.tcl rm CTestTestfile.cmake -rm build.ninja rm VertexCFDConfig.cmake rm -rf Testing cmake \ - -G Ninja \ + -G "$BUILD_SYSTEM" \ -D CMAKE_BUILD_TYPE="$BUILD" \ -D CMAKE_INSTALL_PREFIX="$INSTALL" \ -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ @@ -52,7 +51,7 @@ cmake \ \ ${SOURCE} ``` -Once the configuration is completed, compilation can be initiated with following command. +Please note that `BUILD_SYSTEM` environment can be both `Ninja` or `Make`. Choose the correct one based on your system. Once the configuration is completed, compilation can be initiated with following command. Please note that we used `ninja` in this example so please switch to `make` if your `BUILD_SYSTEM` is defined as `Make`. ``` ninja ``` @@ -60,10 +59,55 @@ By default, `ninja` command will use the available cores, this can be limitted a ``` ninja -j4 ``` -You can replace the number 4 with the number of cores that you prefer. Once the compilation is done, VERTEX-CFD can be installed by using: +You can replace the number 4 with the number of cores that you prefer. We do recommend to use `-j` flag as some systems tends to compile on every CPUs on the login node and fail due to the allocated/used CPUs. Once the compilation is done, VERTEX-CFD can be installed by using: ``` ninja install ``` Once installed, VERTEX-CFD is ready to run. ## GPU installation +For the GPU installation, CUDA needs to be loaded in the HPC environment and Trilinos needs to be built with the GPU support. Just like the CPU version, define the `TRILINOS_HOME` variable as: +``` +TRILINOS_HOME=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA +``` +Once the `TRILINOS_HOME` is set, load the dependencies by including cuda: +``` +module load intel +module load tbb +module load compiler-rt/ +module load mkl +module load python +module load cuda +``` +Once the environment is ready, configuration file can be run as: +``` +./vertexcfd-env-gpu +``` +The content of the `vertexcfd-env-gpu` file is: +``` +#!/bin/sh + +SOURCE=PATH_TO/vertex-cfd +INSTALL=INSTALLATION_PATH +Trilinos_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA +BUILD="Release" +BUILD_SYSTEM=Ninja + +rm -rf CMake* +rm DartConfiguration.tcl +rm CTestTestfile.cmake +rm VertexCFDConfig.cmake +rm -rf Testing + +cmake \ + -G "$BUILD_SYSTEM" \ + -D CMAKE_BUILD_TYPE="$BUILD" \ + -D CMAKE_INSTALL_PREFIX="$INSTALL" \ + -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ + -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ + -D VertexCFD_ENABLE_TESTING=ON \ + -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ + -D Trilinos_ROOT="$Trilinos_ROOT" \ + \ + ${SOURCE} +``` \ No newline at end of file diff --git a/docs/usage.md b/docs/usage.md index 35a97a2..e07d0e6 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -7,13 +7,13 @@ usemathjax: true # Usage -Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, there are two executables, the first one is located in `BUILD_PATH/src/vertexcfd` and the second one is located in `INSTALL_PATH/bin/vertexcfd`. Both of the executables should work. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. +Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, the executable is located in `INSTALL_PATH/bin/vertexcfd`. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. ## Running a simulation In order to run a simulation in serial, vertexcfd can be called directly as: ``` -BUILD_PATH/src/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +INSTALL_PATH/bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml ``` To run in parallel, `mpirun` is required. An example script for the SLURM scheduler is below: ``` @@ -29,7 +29,7 @@ source PATH_TO_ENVIRONMENT_SCRIPT export OMP_PROC_BIND=true export OMP_PLACES=threads -mpirun BUILD_PATH/src/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +mpirun INSTALL_PATH/bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml ``` Once the simulation starts, the example output should look like: ``` @@ -99,4 +99,6 @@ Thu Mar 31 21:51:51 2022 Time integration complete. ============================================================================ ``` -Once the simulation is completed. The results should be ready for visualization. For the visualization, we suggest ParaView. However, any visualization software that supports Exodus format should work. +Once the simulation is completed. The results should be ready for visualization. For the visualization, we suggest ParaView. However, any visualization software that supports Exodus format should work. The example solution file screenshot visualized in Paraview is shown below: + + From 808c8ab3cb74ed947360ed84c09eb9cede044cc4 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Tue, 21 Jan 2025 10:56:16 -0500 Subject: [PATCH 083/214] Add the figure to usage.md --- docs/usage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/usage.md b/docs/usage.md index e07d0e6..6c82246 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -101,4 +101,5 @@ Time integration complete. ``` Once the simulation is completed. The results should be ready for visualization. For the visualization, we suggest ParaView. However, any visualization software that supports Exodus format should work. The example solution file screenshot visualized in Paraview is shown below: +![paraview-ss](https://github.com/user-attachments/assets/75483e4a-cfec-4a51-891f-3b7c72262ba7) From 7101cef9d35f4acdbb1c308a1dfe697bb10278b8 Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 21 Jan 2025 15:55:10 -0500 Subject: [PATCH 084/214] reviewer comments are addressed. --- docs/installation.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index d1cb157..88f107d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -7,11 +7,11 @@ nav_order: 1 VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, please refer to GPU installation. Otherwise, check CPU installation below. ## CPU installation -For the CPU installation, first of all, make sure you have `TRILINOS_HOME` variable defined in your environment. +For the CPU installation, first of all, make sure you have `TRILINOS_ROOT` variable defined in your environment. ``` -TRILINOS_HOME=PATH_TO_TRILINOS/TRILINOS_VERSION +TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION ``` -Once the `TRILINOS_HOME` is set, load the dependencies: +Once the `TRILINOS_ROOT` is set, load the dependencies: ``` module load intel module load tbb @@ -29,7 +29,7 @@ The content of the `vertexcfd-env` file is: SOURCE=PATH_TO/vertex-cfd INSTALL=INSTALLATION_PATH -Trilinos_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION +TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION BUILD="Release" BUILD_SYSTEM=Ninja @@ -47,7 +47,7 @@ cmake \ -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ -D VertexCFD_ENABLE_TESTING=ON \ -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ - -D Trilinos_ROOT="$Trilinos_ROOT" \ + -D TRILINOS_ROOT="$TRILINOS_ROOT" \ \ ${SOURCE} ``` @@ -66,11 +66,11 @@ ninja install Once installed, VERTEX-CFD is ready to run. ## GPU installation -For the GPU installation, CUDA needs to be loaded in the HPC environment and Trilinos needs to be built with the GPU support. Just like the CPU version, define the `TRILINOS_HOME` variable as: +For the GPU installation, CUDA needs to be loaded in the HPC environment and Trilinos needs to be built with the GPU support. Just like the CPU version, define the `TRILINOS_ROOT` variable as: ``` -TRILINOS_HOME=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA +TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA ``` -Once the `TRILINOS_HOME` is set, load the dependencies by including cuda: +Once the `TRILINOS_ROOT` is set, load the dependencies by including cuda: ``` module load intel module load tbb @@ -89,7 +89,7 @@ The content of the `vertexcfd-env-gpu` file is: SOURCE=PATH_TO/vertex-cfd INSTALL=INSTALLATION_PATH -Trilinos_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA +TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA BUILD="Release" BUILD_SYSTEM=Ninja @@ -107,7 +107,7 @@ cmake \ -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ -D VertexCFD_ENABLE_TESTING=ON \ -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ - -D Trilinos_ROOT="$Trilinos_ROOT" \ + -D TRILINOS_ROOT="$TRILINOS_ROOT" \ \ ${SOURCE} ``` \ No newline at end of file From 13f446b8b1274e1371ca6af591f0d2fe1778b49c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 16:29:05 -0500 Subject: [PATCH 085/214] update yml file --- _config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 0421d09..e291ce2 100644 --- a/_config.yml +++ b/_config.yml @@ -3,4 +3,5 @@ logo: docs/figures/vertex_cfd_heated_flow_logo.png description: VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures. show_downloads: true google_analytics: -theme: jekyll-theme-minimal +remote_theme: just-the-docs/just-the-docs +#theme: jekyll-theme-minimal From 35ac190c66d6f5e943313cebb2d051c89fa7e86c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:21:59 -0500 Subject: [PATCH 086/214] update yml file --- _config.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/_config.yml b/_config.yml index e291ce2..0578511 100644 --- a/_config.yml +++ b/_config.yml @@ -5,3 +5,16 @@ show_downloads: true google_analytics: remote_theme: just-the-docs/just-the-docs #theme: jekyll-theme-minimal + +permalink: pretty + +plugins: + - jekyll-remote-theme + - jekyll-seo-tag + - jekyll-feed + +compress_html: + blanklines: true + +# Override purple (default) to ORNL green +color_scheme: ornl_green From ed2016d4a773b079a75d9ae87b213738ba38c5ea Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:23:52 -0500 Subject: [PATCH 087/214] upgrade --- _config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/_config.yml b/_config.yml index 0578511..4eb833f 100644 --- a/_config.yml +++ b/_config.yml @@ -8,11 +8,6 @@ remote_theme: just-the-docs/just-the-docs permalink: pretty -plugins: - - jekyll-remote-theme - - jekyll-seo-tag - - jekyll-feed - compress_html: blanklines: true From 886ce6081d0ae93db5d1be653e791ac511ba97ce Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:24:58 -0500 Subject: [PATCH 088/214] upgrade --- _config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/_config.yml b/_config.yml index 4eb833f..9649fcd 100644 --- a/_config.yml +++ b/_config.yml @@ -8,8 +8,5 @@ remote_theme: just-the-docs/just-the-docs permalink: pretty -compress_html: - blanklines: true - # Override purple (default) to ORNL green color_scheme: ornl_green From 6245c4a64e60899ba510e2c89014ed8037ec85d3 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:26:25 -0500 Subject: [PATCH 089/214] upgrade --- .github/workflows/draft-pdf.yml | 2 +- _config.yml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml index 0fc44ec..1410492 100644 --- a/.github/workflows/draft-pdf.yml +++ b/.github/workflows/draft-pdf.yml @@ -14,7 +14,7 @@ jobs: # This should be the path to the paper within your repo. paper-path: paper/paper.md - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: paper # This is the output path where Pandoc will write the compiled diff --git a/_config.yml b/_config.yml index 9649fcd..fe2b13b 100644 --- a/_config.yml +++ b/_config.yml @@ -6,7 +6,5 @@ google_analytics: remote_theme: just-the-docs/just-the-docs #theme: jekyll-theme-minimal -permalink: pretty - # Override purple (default) to ORNL green color_scheme: ornl_green From 5be2ac33232a7082bd604e3a3f2c92bc27385c37 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:27:35 -0500 Subject: [PATCH 090/214] upgrade --- _config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/_config.yml b/_config.yml index fe2b13b..e291ce2 100644 --- a/_config.yml +++ b/_config.yml @@ -5,6 +5,3 @@ show_downloads: true google_analytics: remote_theme: just-the-docs/just-the-docs #theme: jekyll-theme-minimal - -# Override purple (default) to ORNL green -color_scheme: ornl_green From e9cbb0cd5134e0ab978d32c8f76d41629459b2f8 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 30 Jan 2025 21:32:29 -0500 Subject: [PATCH 091/214] upgrade --- _config.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index e291ce2..607ee03 100644 --- a/_config.yml +++ b/_config.yml @@ -1,7 +1,10 @@ title: VERTEX-CFD -logo: docs/figures/vertex_cfd_heated_flow_logo.png description: VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures. + show_downloads: true google_analytics: -remote_theme: just-the-docs/just-the-docs #theme: jekyll-theme-minimal + +remote_theme: just-the-docs/just-the-docs + +logo: docs/figures/vertex_cfd_heated_flow_logo.png From 45e04fccc750cc2bb4007ae587c99cb0432a8a47 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:23:47 -0500 Subject: [PATCH 092/214] upgrade --- _config.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_config.yml b/_config.yml index 607ee03..99d9fad 100644 --- a/_config.yml +++ b/_config.yml @@ -1,8 +1,11 @@ title: VERTEX-CFD description: VERTEX-CFD is a performance portable software package for computational fluid dynamics (CFD) simulations on CPU or GPU architectures. +baseurl: "/VERTEX-CFD" +url: "https://ORNL.github.io" +repository: ORNL/VERTEX-CFD -show_downloads: true -google_analytics: +#show_downloads: true +#google_analytics: #theme: jekyll-theme-minimal remote_theme: just-the-docs/just-the-docs From 7d3f6ee189bceea125f1d71f2c135e52e2b76d34 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:25:21 -0500 Subject: [PATCH 093/214] upgrade --- _config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_config.yml b/_config.yml index 99d9fad..61cdb89 100644 --- a/_config.yml +++ b/_config.yml @@ -8,6 +8,8 @@ repository: ORNL/VERTEX-CFD #google_analytics: #theme: jekyll-theme-minimal +permalink: pretty + remote_theme: just-the-docs/just-the-docs logo: docs/figures/vertex_cfd_heated_flow_logo.png From 2089a662c5ece919e568e21d53c06e35b896b217 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:27:40 -0500 Subject: [PATCH 094/214] upgrade --- _config.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/_config.yml b/_config.yml index 61cdb89..6fb5ce9 100644 --- a/_config.yml +++ b/_config.yml @@ -12,4 +12,15 @@ permalink: pretty remote_theme: just-the-docs/just-the-docs +plugins: + - jekyll-remote-theme + - jekyll-seo-tag + - jekyll-feed + +compress_html: + blanklines: true + +# Override purple (default) to ORNL green +color_scheme: ornl_green + logo: docs/figures/vertex_cfd_heated_flow_logo.png From de7da4ea46b733aada5d8814b920087ed3f913c1 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:29:29 -0500 Subject: [PATCH 095/214] upgrade --- _config.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/_config.yml b/_config.yml index 6fb5ce9..23df286 100644 --- a/_config.yml +++ b/_config.yml @@ -12,15 +12,9 @@ permalink: pretty remote_theme: just-the-docs/just-the-docs -plugins: - - jekyll-remote-theme - - jekyll-seo-tag - - jekyll-feed - -compress_html: - blanklines: true - -# Override purple (default) to ORNL green -color_scheme: ornl_green - logo: docs/figures/vertex_cfd_heated_flow_logo.png + +# External navigation links +nav_external_links: + - title: VERTEX-CFD on GitHub + url: "https://github.com/ORNL/VERTEX-CFD" From bd0a334745ba6b67dcdfa1a631a345e62b241548 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:31:15 -0500 Subject: [PATCH 096/214] upgrade --- _config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/_config.yml b/_config.yml index 23df286..a5c38e7 100644 --- a/_config.yml +++ b/_config.yml @@ -18,3 +18,11 @@ logo: docs/figures/vertex_cfd_heated_flow_logo.png nav_external_links: - title: VERTEX-CFD on GitHub url: "https://github.com/ORNL/VERTEX-CFD" + +# Aux links for the upper right navigation +aux_links: + "VERTEX-CFD on GitHub": + - "https://github.com/ORNL/VERTEX-CFD" + +# Makes Aux links open in a new tab. Default is false +aux_links_new_tab: false From 0ad42dc3c043b4405ae1eca23ab9c042700e1e6e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:33:18 -0500 Subject: [PATCH 097/214] upgrade --- _config.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/_config.yml b/_config.yml index a5c38e7..b1af488 100644 --- a/_config.yml +++ b/_config.yml @@ -26,3 +26,24 @@ aux_links: # Makes Aux links open in a new tab. Default is false aux_links_new_tab: false + +# Enable callouts in markdown +callouts_level: quiet # or loud +callouts: + highlight: + color: yellow + important: + title: Important + color: blue + new: + title: New + color: green + note: + title: Note + color: purple + warning: + title: Warning + color: red + custom: + title: Note + color: ornl_green From f3ea7a3921d5abd8f67f6e5b69ee32c9287ae4ba Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:37:51 -0500 Subject: [PATCH 098/214] upgrade --- _config.yml | 21 --------- _includes/head_custom.html | 10 ++++ _sass/color_schemes/ornl_green.scss | 4 ++ _sass/custom/custom.scss | 73 +++++++++++++++++++++++++++++ _sass/custom/setup.scss | 4 ++ 5 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 _includes/head_custom.html create mode 100644 _sass/color_schemes/ornl_green.scss create mode 100644 _sass/custom/custom.scss create mode 100644 _sass/custom/setup.scss diff --git a/_config.yml b/_config.yml index b1af488..a5c38e7 100644 --- a/_config.yml +++ b/_config.yml @@ -26,24 +26,3 @@ aux_links: # Makes Aux links open in a new tab. Default is false aux_links_new_tab: false - -# Enable callouts in markdown -callouts_level: quiet # or loud -callouts: - highlight: - color: yellow - important: - title: Important - color: blue - new: - title: New - color: green - note: - title: Note - color: purple - warning: - title: Warning - color: red - custom: - title: Note - color: ornl_green diff --git a/_includes/head_custom.html b/_includes/head_custom.html new file mode 100644 index 0000000..16af50c --- /dev/null +++ b/_includes/head_custom.html @@ -0,0 +1,10 @@ +{% if page.usemathjax %} + + +{% endif %} diff --git a/_sass/color_schemes/ornl_green.scss b/_sass/color_schemes/ornl_green.scss new file mode 100644 index 0000000..fca6234 --- /dev/null +++ b/_sass/color_schemes/ornl_green.scss @@ -0,0 +1,4 @@ +$ornl-green: #007833; +$link-color: $ornl-green; +$btn-primary-color: $ornl-green; +$sidebar-color: #ffffff; diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss new file mode 100644 index 0000000..3f4925d --- /dev/null +++ b/_sass/custom/custom.scss @@ -0,0 +1,73 @@ +.side-bar { + z-index: 0; + display: flex; + flex-wrap: wrap; + background-color: $sidebar-color; + + @include mq(md) { + flex-flow: column nowrap; + position: fixed; + width: $nav-width-md; + height: 100%; + border-right: $border $border-color; + align-items: flex-end; + } + + @include mq(lg) { + width: calc((70% - #{$nav-width + $content-width}) / 20 + #{$nav-width}); + min-width: $nav-width; + } + + & + .main { + @include mq(md) { + margin-left: $nav-width-md; + } + + @include mq(lg) { + // stylelint-disable function-name-case + // disable for Max(), we want to use the CSS max() function + margin-left: Max( + #{$nav-width}, + calc((70% - #{$nav-width + $content-width}) / 20 + #{$nav-width}) + ); + // stylelint-enable function-name-case + } + + .main-header { + display: none; + background-color: $sidebar-color; + + @include mq(md) { + display: flex; + background-color: $body-background-color; + } + + &.nav-open { + display: block; + + @include mq(md) { + display: flex; + } + } + } + } +} + +body { + background-color: $grey-lt-000; +} + +html, body { + height: 100%; +} + +footer { + display: none; +} + +.main { + #background-color: #ffffff; + min-height: 100vh; + background-color: #ffffff; + padding-bottom: 2rem; +} diff --git a/_sass/custom/setup.scss b/_sass/custom/setup.scss new file mode 100644 index 0000000..2e277f6 --- /dev/null +++ b/_sass/custom/setup.scss @@ -0,0 +1,4 @@ +$ornl-green-000: #008542; +$ornl-green-100: #008542; +$ornl-green-200: #008542; +$ornl-green-300: #008542; From e56b8974dbc8442549cbd1b849ae2b61abe62eb5 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:39:29 -0500 Subject: [PATCH 099/214] upgrade --- _config.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/_config.yml b/_config.yml index a5c38e7..d90071c 100644 --- a/_config.yml +++ b/_config.yml @@ -12,6 +12,17 @@ permalink: pretty remote_theme: just-the-docs/just-the-docs +plugins: + - jekyll-remote-theme + - jekyll-seo-tag + - jekyll-feed + +compress_html: + blanklines: true + +# Override purple (default) to ORNL green +color_scheme: ornl_green + logo: docs/figures/vertex_cfd_heated_flow_logo.png # External navigation links @@ -26,3 +37,27 @@ aux_links: # Makes Aux links open in a new tab. Default is false aux_links_new_tab: false + +# Enable callouts in markdown +callouts_level: quiet # or loud +callouts: + highlight: + color: yellow + important: + title: Important + color: blue + new: + title: New + color: green + note: + title: Note + color: purple + warning: + title: Warning + color: red + custom: + title: Note + color: ornl_green + +sass: + custom_stylesheet: _sass/custom/custom.scss From e31e0d4e5f712d762674da34bba6f6a1cba6cc8e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:44:36 -0500 Subject: [PATCH 100/214] upgrade --- Gemfile | 8 ++ Gemfile.lock | 280 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b41a7b4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +source 'https://rubygems.org' +gem 'github-pages', group: :jekyll_plugins + +# Add just-the-docs theme +gem 'just-the-docs' + +# Add spaceship to have mathjax +gem "jekyll-spaceship" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..28cd8dd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,280 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.1.3.3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + base64 (0.2.0) + bigdecimal (3.1.8) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + colorator (1.1.0) + commonmarker (0.23.10) + concurrent-ruby (1.3.1) + connection_pool (2.4.1) + dnsruby (1.72.1) + simpleidn (~> 0.2.1) + drb (2.2.1) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.16.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + execjs (2.9.1) + faraday (2.8.1) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) + ffi (1.17.0) + forwardable-extended (2.6.0) + gemoji (4.1.0) + github-pages (231) + github-pages-health-check (= 1.18.2) + jekyll (= 3.9.5) + jekyll-avatar (= 0.8.0) + jekyll-coffeescript (= 1.2.2) + jekyll-commonmark-ghpages (= 0.4.0) + jekyll-default-layout (= 0.1.5) + jekyll-feed (= 0.17.0) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.16.1) + jekyll-include-cache (= 0.2.1) + jekyll-mentions (= 1.6.0) + jekyll-optional-front-matter (= 0.3.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.16.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.3) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.8.0) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) + jekyll-theme-architect (= 0.2.0) + jekyll-theme-cayman (= 0.2.0) + jekyll-theme-dinky (= 0.2.0) + jekyll-theme-hacker (= 0.2.0) + jekyll-theme-leap-day (= 0.2.0) + jekyll-theme-merlot (= 0.2.0) + jekyll-theme-midnight (= 0.2.0) + jekyll-theme-minimal (= 0.2.0) + jekyll-theme-modernist (= 0.2.0) + jekyll-theme-primer (= 0.6.0) + jekyll-theme-slate (= 0.2.0) + jekyll-theme-tactile (= 0.2.0) + jekyll-theme-time-machine (= 0.2.0) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.13.0) + kramdown (= 2.4.0) + kramdown-parser-gfm (= 1.1.0) + liquid (= 4.0.4) + mercenary (~> 0.3) + minima (= 2.5.1) + nokogiri (>= 1.13.6, < 2.0) + rouge (= 3.30.0) + terminal-table (~> 1.4) + github-pages-health-check (1.18.2) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (>= 4, < 8) + public_suffix (>= 3.0, < 6.0) + typhoeus (~> 1.3) + html-pipeline (2.14.3) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.8.0) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + jekyll (3.9.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (>= 0.7, < 2) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (>= 1.17, < 3) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.8.0) + jekyll (>= 3.0, < 5.0) + jekyll-coffeescript (1.2.2) + coffee-script (~> 2.2) + coffee-script-source (~> 1.12) + jekyll-commonmark (1.4.0) + commonmarker (~> 0.22) + jekyll-commonmark-ghpages (0.4.0) + commonmarker (~> 0.23.7) + jekyll (~> 3.9.0) + jekyll-commonmark (~> 1.4.0) + rouge (>= 2.0, < 5.0) + jekyll-default-layout (0.1.5) + jekyll (>= 3.0, < 5.0) + jekyll-feed (0.17.0) + jekyll (>= 3.7, < 5.0) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.16.1) + jekyll (>= 3.4, < 5.0) + octokit (>= 4, < 7, != 4.4.0) + jekyll-include-cache (0.2.1) + jekyll (>= 3.7, < 5.0) + jekyll-mentions (1.6.0) + html-pipeline (~> 2.3) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-spaceship (0.5.3) + jekyll (>= 3.6, < 5.0) + nokogiri (~> 1.6) + rainbow (~> 3.0) + jekyll-swiss (1.0.0) + jekyll-theme-architect (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.6.0) + jekyll (> 3.5, < 5.0) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.13.0) + gemoji (>= 3, < 5) + html-pipeline (~> 2.2) + jekyll (>= 3.0, < 5.0) + just-the-docs (0.8.2) + jekyll (>= 3.8.5) + jekyll-include-cache + jekyll-seo-tag (>= 2.0) + rake (>= 12.3.1) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.23.1) + mutex_m (0.2.0) + nokogiri (1.15.6-x86_64-linux) + racc (~> 1.4) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (5.0.5) + racc (1.8.0) + rainbow (3.1.1) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rexml (3.2.8) + strscan (>= 3.0.9) + rouge (3.30.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + simpleidn (0.2.3) + strscan (3.1.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (1.8.0) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + github-pages + jekyll-spaceship + just-the-docs + +BUNDLED WITH + 2.4.22 From 1a4dcd6f6cfa432f1e2d5151fa6a8d1c529f4025 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 08:47:29 -0500 Subject: [PATCH 101/214] upgrade --- docs/styles.css | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 docs/styles.css diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 0000000..781b16f --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,46 @@ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #f4f4f4; +} + +header { + background-color: #333; + color: #fff; + padding: 10px 0; + text-align: center; +} + +nav ul { + list-style-type: none; + padding: 0; +} + +nav ul li { + display: inline; + margin: 0 10px; +} + +nav ul li a { + color: #fff; + text-decoration: none; +} + +main { + padding: 20px; +} + +h1, h2 { + color: #333; +} + +footer { + background-color: #333; + color: #fff; + text-align: center; + padding: 10px 0; + position: absolute; + bottom: 0; + width: 100%; +} From 39629a75d81bf207038609b4b8fd966eedb8174c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 09:03:33 -0500 Subject: [PATCH 102/214] upgrade --- docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md | 4 ++-- docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md | 4 ++-- docs/run-vertexcfd/run-incompressible-channel.md | 2 +- index.md | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md b/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md index dbe17d6..1bd1b0e 100644 --- a/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md +++ b/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md @@ -1,5 +1,5 @@ -# Building VERTEX-CFD on NARSIL CPU architecture -These instructions detail the process for building VERTEX-CFD for use on the NARSIL mod cluster CPU nodes. Similar instructions for a GPU build can be found [here](./install-vertexcfd-on-narsil-gpu.md). +# Building VERTEX-CFD on CPU architecture +These instructions detail the process for building VERTEX-CFD for use on the mod cluster CPU nodes. Similar instructions for a GPU build can be found [here](./install-vertexcfd-on-narsil-gpu.md). ## Initial Build First, create project directories to clone the source repository and compile the code: diff --git a/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md b/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md index 9c27ce4..5bea18d 100644 --- a/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md +++ b/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md @@ -1,5 +1,5 @@ -# Building VERTEX-CFD on NARSIL GPU architecture -These instructions detail the process for building VERTEX-CFD in a CUDA-enabled form for use on the NARSIL mod cluster GPU nodes. Similar instructions for a basic CPU build can be found [here](./install-vertexcfd-on-narsil-cpu.md). +# Building VERTEX-CFD on GPU architecture +These instructions detail the process for building VERTEX-CFD in a CUDA-enabled form for use on the mod cluster GPU nodes. Similar instructions for a basic CPU build can be found [here](./install-vertexcfd-on-narsil-cpu.md). ## Initial Build First, be sure you are logged into a GPU node to build the code: diff --git a/docs/run-vertexcfd/run-incompressible-channel.md b/docs/run-vertexcfd/run-incompressible-channel.md index c1191c1..3055046 100644 --- a/docs/run-vertexcfd/run-incompressible-channel.md +++ b/docs/run-vertexcfd/run-incompressible-channel.md @@ -93,6 +93,6 @@ Time integration complete. ============================================================================ ``` -Once the job completed, the numerical solution can be read from the Exodus file `incompressible_2d_channel_solution.exo` using ParaView, through NoMachine client. ParaView is available on NARSIL at`/projects/vertex/opt/paraview/5.11.0/bin/paraview`. +Once the job completed, the numerical solution can be read from the Exodus file `incompressible_2d_channel_solution.exo` using ParaView. ![](uploads/run-incompressible-2d-channel/temperature-profile-paraview.png)*Visualization of the temperature profile predicted by VertexCFD with ParaView.* diff --git a/index.md b/index.md index 3e11f28..ab3a7e9 100644 --- a/index.md +++ b/index.md @@ -10,7 +10,7 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } -[View it on GitHub][AdditiveFOAM repo]{: .btn .fs-5 .mb-4 .mb-md-0 } +[View it on GitHub][VERTEX-CFD repo]{: .btn .fs-5 .mb-4 .mb-md-0 } --- @@ -38,4 +38,4 @@ If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBE doi = {DOI_NUMBER}, url = {DOI_URL} } -``` \ No newline at end of file +``` From baffe5bf335fc9c5be062e7ac6a53d92669b2f91 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 09:07:42 -0500 Subject: [PATCH 103/214] upgrade --- index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.md b/index.md index ab3a7e9..4e16cda 100644 --- a/index.md +++ b/index.md @@ -39,3 +39,5 @@ If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBE url = {DOI_URL} } ``` + +[VERTEX-CFD repo]: https://github.com/ORNL/VERTEX-CFD From 597488b5174bff66b8a72fbc5425b94e2ffe7306 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 09:09:48 -0500 Subject: [PATCH 104/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 4e16cda..aa9b3de 100644 --- a/index.md +++ b/index.md @@ -10,7 +10,7 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } -[View it on GitHub][VERTEX-CFD repo]{: .btn .fs-5 .mb-4 .mb-md-0 } +[View it on GitHub](https://github.com/ORNL/AdditiveFOAM){: .btn .fs-5 .mb-4 .mb-md-0 } --- From 12a3149b195d5dc03dd4de3d50399692e7b5c1b9 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 09:17:37 -0500 Subject: [PATCH 105/214] upgrade --- index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/index.md b/index.md index aa9b3de..0e7a33c 100644 --- a/index.md +++ b/index.md @@ -9,7 +9,6 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o {: .fs-6 .fw-300 } [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } - [View it on GitHub](https://github.com/ORNL/AdditiveFOAM){: .btn .fs-5 .mb-4 .mb-md-0 } --- From 18d30f82a27f1e282ae9f6d5761284a4feb2022a Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 09:19:45 -0500 Subject: [PATCH 106/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 0e7a33c..41e4013 100644 --- a/index.md +++ b/index.md @@ -9,7 +9,7 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o {: .fs-6 .fw-300 } [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } -[View it on GitHub](https://github.com/ORNL/AdditiveFOAM){: .btn .fs-5 .mb-4 .mb-md-0 } +[View it on GitHub](https://github.com/ORNL/VERTEX-CFD){: .btn .fs-5 .mb-4 .mb-md-0 } --- From 8b4116b86ab6642aafa4cfa7be1d70e4e105dda2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 10:22:30 -0500 Subject: [PATCH 107/214] upgrade --- docs/installation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 88f107d..46d1825 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,5 +1,6 @@ --- parent: VERTEX-CFD v1.0 User Guide +title: Installation nav_order: 1 --- @@ -110,4 +111,4 @@ cmake \ -D TRILINOS_ROOT="$TRILINOS_ROOT" \ \ ${SOURCE} -``` \ No newline at end of file +``` From 1de80d1cafb0b43bb1d699ea5d3ca67d91b802a4 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 10:31:29 -0500 Subject: [PATCH 108/214] upgrade --- .../gitLab-configuration-on-narsil.md | 0 .../install-vertexcfd-on-narsil-cpu.md | 0 .../install-vertexcfd-on-narsil-gpu.md | 0 .../sbatch_submission_script | 0 .../temperature-profile-paraview.png | Bin .../sbatch_submission_script | 0 .../trilinos-pardiso-narsil.sh | 0 .../vertexcfd-pardiso-narsil.sh | 0 ...using-epetra-and-pardiso-solvers-in-vertexcfd.md | 0 .../run-vertexcfd/run-incompressible-channel.md | 0 {docs => saveDocs}/run-vertexcfd/run-vertexcfd.md | 0 .../temperature-profile-paraview.png | Bin .../regression-test-suite-for-vertexcfd.md | 0 .../software-quality-assurance.md | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename {docs => saveDocs}/install-vertexcfd/gitLab-configuration-on-narsil.md (100%) rename {docs => saveDocs}/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md (100%) rename {docs => saveDocs}/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md (100%) rename {docs => saveDocs}/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script (100%) rename {docs => saveDocs}/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png (100%) rename {docs => saveDocs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script (100%) rename {docs => saveDocs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh (100%) rename {docs => saveDocs}/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh (100%) rename {docs => saveDocs}/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md (100%) rename {docs => saveDocs}/run-vertexcfd/run-incompressible-channel.md (100%) rename {docs => saveDocs}/run-vertexcfd/run-vertexcfd.md (100%) rename {docs => saveDocs}/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png (100%) rename {docs => saveDocs}/software-quality-assurance/regression-test-suite-for-vertexcfd.md (100%) rename {docs => saveDocs}/software-quality-assurance/software-quality-assurance.md (100%) diff --git a/docs/install-vertexcfd/gitLab-configuration-on-narsil.md b/saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md similarity index 100% rename from docs/install-vertexcfd/gitLab-configuration-on-narsil.md rename to saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md diff --git a/docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md b/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md similarity index 100% rename from docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md rename to saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md diff --git a/docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md b/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md similarity index 100% rename from docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md rename to saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md diff --git a/docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script b/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script similarity index 100% rename from docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script rename to saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script diff --git a/docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png b/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png similarity index 100% rename from docs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png rename to saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png diff --git a/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script b/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script similarity index 100% rename from docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script rename to saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script diff --git a/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh b/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh similarity index 100% rename from docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh rename to saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh diff --git a/docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh b/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh similarity index 100% rename from docs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh rename to saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh diff --git a/docs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md b/saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md similarity index 100% rename from docs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md rename to saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md diff --git a/docs/run-vertexcfd/run-incompressible-channel.md b/saveDocs/run-vertexcfd/run-incompressible-channel.md similarity index 100% rename from docs/run-vertexcfd/run-incompressible-channel.md rename to saveDocs/run-vertexcfd/run-incompressible-channel.md diff --git a/docs/run-vertexcfd/run-vertexcfd.md b/saveDocs/run-vertexcfd/run-vertexcfd.md similarity index 100% rename from docs/run-vertexcfd/run-vertexcfd.md rename to saveDocs/run-vertexcfd/run-vertexcfd.md diff --git a/docs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png b/saveDocs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png similarity index 100% rename from docs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png rename to saveDocs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png diff --git a/docs/software-quality-assurance/regression-test-suite-for-vertexcfd.md b/saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md similarity index 100% rename from docs/software-quality-assurance/regression-test-suite-for-vertexcfd.md rename to saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md diff --git a/docs/software-quality-assurance/software-quality-assurance.md b/saveDocs/software-quality-assurance/software-quality-assurance.md similarity index 100% rename from docs/software-quality-assurance/software-quality-assurance.md rename to saveDocs/software-quality-assurance/software-quality-assurance.md From e420c85932216b14ecb14dfc270160b24e754669 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 11:36:11 -0500 Subject: [PATCH 109/214] upgrade --- .../gitLab-configuration-on-narsil.md | 6 - .../install-vertexcfd-on-narsil-cpu.md | 85 ---------- .../install-vertexcfd-on-narsil-gpu.md | 97 ----------- .../sbatch_submission_script | 41 ----- .../temperature-profile-paraview.png | Bin 534430 -> 0 bytes .../sbatch_submission_script | 57 ------- .../trilinos-pardiso-narsil.sh | 92 ----------- .../vertexcfd-pardiso-narsil.sh | 25 --- ...epetra-and-pardiso-solvers-in-vertexcfd.md | 151 ------------------ .../run-incompressible-channel.md | 98 ------------ saveDocs/run-vertexcfd/run-vertexcfd.md | 3 - .../temperature-profile-paraview.png | Bin 1774360 -> 0 bytes .../regression-test-suite-for-vertexcfd.md | 104 ------------ .../software-quality-assurance.md | 5 - 14 files changed, 764 deletions(-) delete mode 100644 saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md delete mode 100644 saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md delete mode 100644 saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md delete mode 100644 saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script delete mode 100644 saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png delete mode 100644 saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script delete mode 100644 saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh delete mode 100644 saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh delete mode 100644 saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md delete mode 100644 saveDocs/run-vertexcfd/run-incompressible-channel.md delete mode 100644 saveDocs/run-vertexcfd/run-vertexcfd.md delete mode 100644 saveDocs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png delete mode 100644 saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md delete mode 100644 saveDocs/software-quality-assurance/software-quality-assurance.md diff --git a/saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md b/saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md deleted file mode 100644 index 8c89ae6..0000000 --- a/saveDocs/install-vertexcfd/gitLab-configuration-on-narsil.md +++ /dev/null @@ -1,6 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) - -This page provides tips on how to set up your local environment to work with GitLab on Narsil: - -- Error message: `github error: unable to read askpass response from gnome-ssh-askpass`. Add `unset SSH_ASKPASS` to your `.bashrc`. -- Prompt for password: add your ssh key to your GitLab repo following the steps detailed in [here](https://code-int.ornl.gov/-/profile/keys). diff --git a/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md b/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md deleted file mode 100644 index 1bd1b0e..0000000 --- a/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md +++ /dev/null @@ -1,85 +0,0 @@ -# Building VERTEX-CFD on CPU architecture -These instructions detail the process for building VERTEX-CFD for use on the mod cluster CPU nodes. Similar instructions for a GPU build can be found [here](./install-vertexcfd-on-narsil-gpu.md). - -## Initial Build -First, create project directories to clone the source repository and compile the code: -``` -cd -mkdir -p projects/build -cd projects -``` - -Then, clone the source code from the internal ORNL GitLab repository. Enter your credentials if prompted. - -``` -git clone git@code-int.ornl.gov:vertex/vertex-cfd.git -``` - -Any edits made to the code will be done in the source directory created by the clone operation. However, the code will be compiled in the `build` directory which was created in the previous step. - -The repository contains a configuration file which loads the required modules to compile and use VERTEX-CFD. This file can be sourced via `~/projects/vertex-cfd/scripts/ci/vertexcfd-env.sh`, however it is often more convenient to copy this file to your home directory: - -``` -cp ~/projects/vertex-cfd/scripts/ci/vertexcfd-env.sh ~/.vertexcfd-env -``` - -The environment for VERTEX-CFD can then be configured with: - -``` -source ~/.vertexcfd-env -``` - -Once the environment is configured, you are ready to build. Change to the build directory to begin: - -``` -cd ~/projects/build -``` - -Now, copy the build scripts from the repository. You will use the CPU version of the build script for the CPU nodes. - -``` -cp ../vertex-cfd/scripts/build/vertex_narsil_cpu_release.sh . -``` - -Ensure that `SOURCE` and `INSTALL` within the build script point to the desired source and installation directories, then execute the script. - -``` -./vertex_narsil_cpu_release.sh -``` - -This will copy the necessary files to your build directory. Now, the source code is ready to be compiled and installed. Note that you will use `ninja` instead of `make` for compilation on the CPU node. - -``` -ninja -ninja install -``` - -If the code compiles without error, it is ready to use. Executables are located within the `bin` directory of the build location. The `build` directory should be reserved for testing, while production runs should be carried out in `install` or another location. - -## Rebuilding After Changes -After making changes or pulling updates from the head repository, you will again recompile the code from the build directory. - -``` -cd ~/projects/build -``` - -If any files have been created or removed since the previous build, rerun the build script: - -``` -./vertex_narsil_cuda_release.sh -``` - -Then, compile and install: - -``` -ninja -ninja install -``` - -If no errors are given, your code has successfully rebuilt with the desired changes and is ready to use. **Prior to pushing any changes to the repository, be sure to run the formatting tool:** - -``` -ninja format -``` - -## [RUNNING CASES WITH VERTEX-CFD](../run-vertexcfd/run-vertexcfd.md) diff --git a/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md b/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md deleted file mode 100644 index 5bea18d..0000000 --- a/saveDocs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md +++ /dev/null @@ -1,97 +0,0 @@ -# Building VERTEX-CFD on GPU architecture -These instructions detail the process for building VERTEX-CFD in a CUDA-enabled form for use on the mod cluster GPU nodes. Similar instructions for a basic CPU build can be found [here](./install-vertexcfd-on-narsil-cpu.md). - -## Initial Build -First, be sure you are logged into a GPU node to build the code: - -``` -ssh narsil-gpu-login -``` - -Enter your login credentials when prompted. Then, create project directories to clone the source repository and compile the code: -``` -cd -mkdir -p projects/build -cd projects -``` - -Next, clone the repository from the internal ORNL GitLab repository. Enter your credentials if prompted. - -``` -git clone git@code-int.ornl.gov:vertex/vertex-cfd.git -``` - -Any edits made to the code will be done in the source directory created by the clone operation. However, the code will be compiled in the `build` directory which was created in the previous step. - -The repository contains a configuration file which loads the required modules to compile and use VERTEX-CFD. This file can be sourced via `~/projects/vertex-cfd/scripts/ci/vertexcfd-env.sh`, however it is often more convenient to copy this file to your home directory: - -``` -cp ~/projects/vertex-cfd/scripts/ci/vertexcfd-env.sh ~/.vertexcfd-env -``` - -The environment for VERTEX-CFD can then be configured with: - -``` -source ~/.vertexcfd-env -``` - -For GPU compilation, you will also have to load the CUDA module: - -``` -module load cuda/cuda-11.4 -``` - -Once the environment is configured, you are ready to build. Change to the build directory to begin: - -``` -cd ~/projects/build -``` - -Now, copy the build scripts from the repository. You will use the CUDA version of the build script for the GPU-enabled solver. - -``` -cp ../vertex-cfd/scripts/build/vertex_narsil_cuda_release.sh . -``` - -Ensure that `SOURCE` and `INSTALL` within the build script point to the desired source and installation directories, then execute the script. - -``` -./vertex_narsil_cuda_release.sh -``` - -This will copy the necessary files to your build directory. Now, the source code is ready to be compiled and installed. Note that you will use `make` instead of `ninja` for compilation on the GPU node. Be sure to use the `-j` flag to build in parallel, but do not use all available resources on the login node. - -``` -make -j 32 -make install -``` - -If the code compiles without error, it is ready to use. Executables are located within the `bin` directory of the build location. The `build` directory should be reserved for testing, while production runs should be carried out in `install` or another location. - -## Rebuilding After Changes -After making changes or pulling updates from the head repository, you will again recompile the code from the build directory. - -``` -cd ~/projects/build -``` - -If any files have been created or removed since the previous build, rerun the build script: - -``` -./vertexcfd_narsil_cuda_release.sh -``` - -Then, compile and install: - -``` -make -make install -``` - -If no errors are given, your code has successfully rebuilt with the desired changes and is ready to use. **Prior to pushing any changes to the repository, be sure to run the formatting tool:** - -``` -make format -``` - -## [RUNNING CASES WITH VERTEX-CFD](../run-vertexcfd/run-vertexcfd.md) diff --git a/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script b/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script deleted file mode 100644 index de780f4..0000000 --- a/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/sbatch_submission_script +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/bash -#SBATCH -p vertex -#SBATCH -J channel-test -#SBATCH --nodes 1 -#SBATCH --ntasks-per-node 10 -#SBATCH --cpus-per-task 1 -#SBATCH -o output%j.txt -#SBATCH -e error%j.txt -#SBATCH --mail-type=ALL -#SBATCH --mail-user=mxd@ornl.gov -#SBATCH -t 01:00:00 - -source ~/.vertexcfd-env - -export OMP_PROC_BIND=true -export OMP_PLACES=threads - -export IOSS_PROPERTIES="COMPOSE_RESULTS=on:MINIMIZE_OPEN_FILES=on:MAXIMUM_NAME_LENGTH=64" -export EXODUS_NETCDF4=1 - -cd $SLURM_SUBMIT_DIR - -exec_path="../bin/vertexcfd" - -echo " -SLURM_JOB_ID -$SLURM_JOB_ID - -SLURM_JOB_NUM_NODES -$SLURM_JOB_NUM_NODES - -SLURM_NTASKS -$SLURM_NTASKS - -SLURM_JOB_NODELIST -$SLURM_JOB_NODELIST -" - -inputfile=incompressible_2d_channel.xml - -mpirun $exec_path --i=$inputfile --kokkos-threads=1 diff --git a/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png b/saveDocs/install-vertexcfd/uploads/installation-of-vertexcfd-on-narsil/temperature-profile-paraview.png deleted file mode 100644 index 9f4ea1467a47d019fb78e58f4db1a43526abf026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534430 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⋙̸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

gx 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�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%0A}?P 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�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Ý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 O0000 -INPUTFILE= -mpirun --map-by ${MAP_STRING} $EXE --i=$INPUTFILE - diff --git a/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh b/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh deleted file mode 100644 index e65a3a6..0000000 --- a/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/sh - -# Update these paths -SOURCE= -INSTALL= - -BUILD="Release" - -# Load relevant environment modules -module use /projects/mp_common/spack_env/v0.4/modulefiles -module load gcc/gcc-11.2.0 -module load cmake/cmake-3.21.3 - -module use /projects/mp_common/spack_env/v0.4/spack/share/spack/modules/linux-rhel8-zen2/ -module load openmpi/4.1.1 -module load ninja/1.10.1 -module load boost/1.74.0 netcdf-c/4.7.4 -module load parmetis/4.0.3 -module load intel-oneapi-mkl - -cmake \ - -G Ninja \ - -D CMAKE_BUILD_TYPE:STRING="$BUILD" \ - -D CMAKE_INSTALL_PREFIX:PATH=$INSTALL \ - -D CMAKE_CXX_FLAGS="-Wno-implicit-int-float-conversion -Wno-user-defined-warnings" \ - -D CMAKE_CXX_STANDARD=14 \ - -D CMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -D BUILD_SHARED_LIBS=ON \ - -D TPL_ENABLE_MPI=ON \ - -D TPL_ENABLE_BLAS=ON \ - -D TPL_ENABLE_LAPACK=ON \ - -D TPL_ENABLE_HDF5=ON \ - -D TPL_ENABLE_Netcdf=ON \ - -D TPL_ENABLE_Boost=ON \ - -D TPL_ENABLE_BoostLib=ON \ - -D TPL_ENABLE_Matio=OFF \ - -D TPL_ENABLE_METIS=ON \ - -D TPL_ENABLE_ParMETIS=ON \ - -D TPL_ENABLE_MKL=ON \ - -D BLAS_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D BLAS_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D LAPACK_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D LAPACK_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D MKL_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D TPL_ENABLE_PARDISO_MKL=ON \ - -D MKL_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D MKL_INCLUDE_DIRS="${MKLROOT}/include" \ - -D PARDISO_MKL_INCLUDE_DIRS="${MKLROOT}/include" \ - -D PARDISO_MKL_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D Amesos_ENABLE_PARDISO_MKL=ON \ - -D Trilinos_ENABLE_EXPLICIT_INSTANTIATION=ON \ - -D Trilinos_ENABLE_ALL_PACKAGES=OFF \ - -D Trilinos_ENABLE_ALL_OPTIONAL_PACKAGES=OFF \ - -D Trilinos_ENABLE_TESTS=OFF \ - -D Trilinos_ENABLE_EXAMPLES=OFF \ - -D Trilinos_ENABLE_OpenMP=ON \ - -D Trilinos_ENABLE_Kokkos=ON \ - -D Kokkos_ENABLE_SERIAL=ON \ - -D Kokkos_ENABLE_OPENMP=ON \ - -D Kokkos_ENABLE_CUDA=OFF \ - -D Kokkos_ARCH_EPYC=ON \ - -D Trilinos_ENABLE_KokkosKernels=ON \ - -D Trilinos_ENABLE_Sacado=ON \ - -D Trilinos_ENABLE_Tpetra=ON \ - -D Tpetra_INST_COMPLEX_DOUBLE=OFF \ - -D Tpetra_INST_COMPLEX_FLOAT=OFF \ - -D Tpetra_INST_SERIAL=ON \ - -D Tpetra_INST_OPENMP=ON \ - -D Tpetra_INST_CUDA=OFF \ - -D Trilinos_ENABLE_Intrepid2=ON \ - -D Trilinos_ENABLE_Belos=ON \ - -D Trilinos_ENABLE_Ifpack2=ON \ - -D Trilinos_ENABLE_Amesos2=ON \ - -D Trilinos_ENABLE_MueLu=ON \ - -D Trilinos_ENABLE_NOX=ON \ - -D Trilinos_ENABLE_SEACAS=ON \ - -D Trilinos_ENABLE_STKMesh=ON \ - -D Trilinos_ENABLE_STKIO=ON \ - -D Trilinos_ENABLE_Zoltan2=ON \ - -D Trilinos_ENABLE_Tempus=ON \ - -D Trilinos_ENABLE_Piro=ON \ - -D Trilinos_ENABLE_PanzerCore=ON \ - -D Trilinos_ENABLE_PanzerDofMgr=ON \ - -D Trilinos_ENABLE_PanzerDiscFE=ON \ - -D Trilinos_ENABLE_PanzerAdaptersSTK=ON \ - -D Trilinos_ENABLE_PanzerMiniEM=ON \ - -D Trilinos_ENABLE_PanzerExprEval=ON \ - -D Panzer_ENABLE_EXAMPLES=ON \ - -D TPL_ENABLE_gtest=OFF \ - -D Trilinos_ENABLE_Gtest=OFF \ - ${SOURCE} - diff --git a/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh b/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh deleted file mode 100644 index 4b29c2a..0000000 --- a/saveDocs/install-vertexcfd/uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh +++ /dev/null @@ -1,25 +0,0 @@ -SOURCE= -INSTALL= - -BUILD="Release" - -rm -rf CMake* - -# Unset variable set by spack modules. -# If this is present, any directories present will be treated -# as "implicit" library paths by CMake and it will strip them -# out of the executable RPATHS. Then you have to set -# LD_LIBRARY_PATH appropriately to run jobs. -unset LIBRARY_PATH - -cmake \ - -G Ninja \ - -D CMAKE_BUILD_TYPE="$BUILD" \ - -D CMAKE_INSTALL_PREFIX="$INSTALL" \ - -D VertexCFD_ENABLE_COVERAGE_BUILD=ON \ - -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ - -D CLANG_FORMAT_EXECUTABLE="/usr/bin/clang-format" \ - -D Trilinos_DIR=/projects/mp_common/spack_env/v0.4/trilinos/13.2.0-mkl-complex/lib/cmake/Trilinos \ - -D VertexCFD_ENABLE_TESTING=ON \ - \ - ${SOURCE} diff --git a/saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md b/saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md deleted file mode 100644 index 6758a70..0000000 --- a/saveDocs/install-vertexcfd/using-epetra-and-pardiso-solvers-in-vertexcfd.md +++ /dev/null @@ -1,151 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) - -This page describes the steps necessary to build and execute VertexCFD using the Epetra solver stack within Trilinos. Epetra represents the legacy software stack in Trilinos, supporting only CPU execution (no GPU support) and only double precision arithmetic. In contrast, the Tpetra software stack (the default in VertexCFD) supports GPU execution and arbitrary data types, including support for automatic differentiation types. However, Epetra has been in use much longer and is therefore generally more robust, has more solver options available, and in some cases offers improved performance. We will be working with the Trilinos development team to improve the capabilities of the Tpetra solvers to attempt to ultimately match the Epetra features, but at the current time there are likely benefits to using Epetra instead of Tpetra. - -
-Building Trilinos with Pardiso support - -**On Narsil, this step can be skipped and you can simply use existing Trilinos installations with Pardiso support located at `/projects/mp_common/spack_env/v0.4/trilinos/13.2.0-mkl-complex/lib/cmake/Trilinos`.** - -The primary reason for using Epetra is to enable the usage of the Pardiso sparse direct solver, which is both very efficient and supports CPU multithreading. First, the Intel OneAPI library (which contains the Math Kernel Library, or MKL) should be installed. This is most easily installed through the `intel-oneapi-mkl` Spack package. This is already installed on the Narsil machine and can be loaded with: -```plaintext -module load intel-oneapi-mkl -``` -The same module is also available on Narsil: -```plaintext -module load intel-oneapi-mkl/2021.1.1 -``` -Next, Trilinos must be built with support for MKL and Pardiso. For a new build of Trilinos, it is necessary to add the following options to an existing Trilinos CMake configure script (full configure script is attached below): - -```plaintext - -D TPL_ENABLE_MKL=ON \ - -D BLAS_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D BLAS_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D LAPACK_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D LAPACK_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D MKL_LIBRARY_NAMES="mkl_intel_lp64;mkl_gnu_thread;mkl_core" \ - -D TPL_ENABLE_PARDISO_MKL=ON \ - -D MKL_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D MKL_INCLUDE_DIRS="${MKLROOT}/include" \ - -D PARDISO_MKL_INCLUDE_DIRS="${MKLROOT}/include" \ - -D PARDISO_MKL_LIBRARY_DIRS="${MKLROOT}/lib/intel64" \ - -D Amesos_ENABLE_PARDISO_MKL=ON \ -``` - -Note that `MKLROOT` is an environment variable set by the `intel-oneapi-mkl` module. - -
-
-Building VertexCFD with MKL-enabled Trilinos -Nothing special is required from VertexCFD -- simply reference an install of Trilinos that has MKL and Pardiso enabled. The following works on Narsil (full script also attached below) and is already part of the [installation workflow for VertexCFD](doc/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md): - -```plaintext -# Set these to the location of VertexCFD source and the desired install location -SOURCE= -INSTALL= -BUILD="Release" - -export TRILINOS_DIR=/projects/mp_common/spack_env/v0.4/trilinos/13.2.0-mkl-complex/lib/cmake/Trilinos -export CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}:${TRILINOS_DIR} - -cmake \ - -G Ninja \ - -D CMAKE_BUILD_TYPE="$BUILD" \ - -D CMAKE_INSTALL_PREFIX="$INSTALL" \ - -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ - -D BUILD_SHARED_LIBS=ON \ - -D CLANG_FORMAT_EXECUTABLE="/usr/bin/clang-format" \ - -D VertexCFD_ENABLE_TESTING=ON \ - ${SOURCE} -``` - -
-
-Enabling the Epetra and Pardiso solvers - -From an existing VertexCFD XML input file, a few changes are necessary to use Epetra/Pardiso (see `examples/inputs/tp7_epetra.xml` for a working example): - -1. In the `"User Data"` parameter list, set the linear algebra type to Epetra: - - ```plaintext - - ``` -2. In the `"Linear Solver"` list, set the preconditioner type to Ifpack (rather than Ifpack2): - - ```plaintext - - ``` - - Failure to do this will result in a fairly incomprehensible message similar to: - - ```plaintext - terminate called after throwing an instance of 'std::logic_error' - what(): /home/hu4/Codes/Trilinos/trilinos/packages/ifpack2/adapters/thyra/Thyra_Ifpack2PreconditionerFactory_def.hpp:122: - - Throw number = 1 - - Throw test that evaluated to true: !(this->isCompatible(*fwdOpSrc)) - - Error! - ``` -3. In the `"Preconditioner Types"` list, add the following block: - - ```plaintext - - - - - - - - ``` - - This will set the additive Schwarz overlap to 1 (highly recommended) as well as enable the Pardiso sparse direct solver. -
-
-Launching VertexCFD with multithreading - -The ability of VertexCFD to use shared-memory parallelism (i.e., multithreading) depends strongly on the solver and preconditioner selection. At the current time, the Pardiso sparse direct solver is the only option that is able to use multithreading in a meaningful way. Using multithreading (instead of exclusively MPI-based parallelism) allows the linear solver to maintain larger subdomains when constructing a preconditioner, leading to smaller iteration counts from the linear solver and greater robustness. The impact on _runtime_ is problem-dependent. On Narsil (or other Slurm/sbatch systems), the following modifications can be made to an `sbatch` submission script to enable multithreading: - -1. Request multiple CPU cores per MPI task. This is accomplished using the `--ntasks-per-node` and `--cpus-per-task` options in the sbatch resource request. For example, the following lines will request 32 MPI ranks per node with 4 threads per rank (thus using 128 CPU cores per node): - - ``` - #SBATCH --ntasks-per-node 32 - #SBATCH --cpus-per-task 4 - ``` - - In general, the number of tasks per node multiplied by the number of CPUs per task should equal the number of physical CPU cores on a node. -2. Set the number of OpenMP tasks per rank. Both Trilinos and Pardiso use OpenMP for multithreading. The number of threads per task can be set by including the following lines in the submission script anywhere before the executable is launched: - - ``` - export OMP_PROC_BIND=true - export OMP_PLACES=cores - export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK - ``` - - The first two lines are optional, but will silence warnings from Kokkos. The final line tells OpenMP to use all of the cores allocated by Slurm for multithreading. -3. Set the MPI process binding for launching the executable. Depending on how Slurm is configured, the default assignment of threads to CPU cores may not work well (multiple threads may be placed on the same physical core, leading to significantly reduced performance). On Narsil, the following block will launch a job with correct binding of threads: - - ``` - # Special case for single MPI rank per node - if [ ${SLURM_NTASKS_PER_NODE} -eq 1 ] - then - MAP_STRING="ppr:1:node:pe=${SLURM_CPUS_PER_TASK}" - else - # Compute MPI mapping -- assuming a node has 2 sockets - NUM_SOCKETS=2 - let "TASKS_PER_SOCKET = ${SLURM_NTASKS_PER_NODE} / ${NUM_SOCKETS}" - MAP_STRING="ppr:${TASKS_PER_SOCKET}:socket:pe=${SLURM_CPUS_PER_TASK}" - fi - -The full sbatch submission script is included below. - -
- -[sbatch_submission_script](uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/sbatch_submission_script) - -[vertexcfd-pardiso-narsil.sh](uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/vertexcfd-pardiso-narsil.sh) - -[trilinos-pardiso-narsil.sh](uploads/using-epetra-and-pardiso-solvers-in-vertexcfd/trilinos-pardiso-narsil.sh) - -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) diff --git a/saveDocs/run-vertexcfd/run-incompressible-channel.md b/saveDocs/run-vertexcfd/run-incompressible-channel.md deleted file mode 100644 index 3055046..0000000 --- a/saveDocs/run-vertexcfd/run-incompressible-channel.md +++ /dev/null @@ -1,98 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertexcfd/-/wikis/home) -> [Run VertexCFD](run-vertexcfd.md) - -Once VertexCFD is installed, an incompressible flow in a 2D channel is available in `installation_path/examples/incompressible/incompressible_2d_channel.xml`. The input file relies on a `xml` format. The example can be run with the command line `installation_path/bin/vertexcfd --i=incompressible_2d_channel.xml` and should produce the following output: - -``` -Kokkos::OpenMP::initialize WARNING: OMP_PROC_BIND environment variable not set - In general, for best performance with OpenMP 4.0 or better set OMP_PROC_BIND=spread and OMP_PLACES=threads - For best performance with OpenMP 3.1 set OMP_PROC_BIND=true - For unit testing set OMP_PROC_BIND=false - -============================================================================ -Time Integration Begin -Thu Mar 31 21:46:09 2022 - - Stepper = Backward Euler - Simulation Time Range [0, 0.2] ----------------------------------------------------------------------------- - -Time Step = 1; Order = 1 -CFL = 1.000e+00; dt = 6.186e-03; Time = 0.00000e+00 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 6.65e-02 - 1 2.03e-02 1 6.45e-16 - 2 4.66e-04 1 4.25e-16 - 3 5.40e-07 1 5.36e-16 - 4 3.19e-13 1 6.58e-16 -Time step time to completion (s): 5.97e+00 - -Time Step = 2; Order = 1 -CFL = 1.000e+00; dt = 5.644e-03; Time = 6.18583e-03 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 5.73e-02 - 1 5.72e-03 1 4.68e-16 - 2 5.40e-05 1 4.01e-16 - 3 3.47e-09 1 7.18e-16 -Time step time to completion (s): 8.96e+00 - -Time Step = 3; Order = 1 -CFL = 1.000e+00; dt = 5.385e-03; Time = 1.18299e-02 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 5.37e-02 - 1 4.14e-03 1 4.17e-16 - 2 3.35e-05 1 3.47e-16 - 3 2.01e-09 1 5.54e-16 -Time step time to completion (s): 7.03e-01 - -Time Step = 4; Order = 1 -CFL = 1.000e+00; dt = 5.249e-03; Time = 1.72150e-02 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 5.28e-02 - 1 3.20e-03 1 4.06e-16 - 2 1.69e-05 1 4.46e-16 - 3 2.15e-10 1 4.99e-16 -Time step time to completion (s): 1.19e+00 -... ... -... ... -... ... -... ... -Time Step = 39; Order = 1 -CFL = 1.000e+00; dt = 4.853e-03; Time = 1.88877e-01 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 3.59e-02 - 1 1.16e-03 1 3.55e-16 - 2 1.69e-06 1 3.86e-16 - 3 8.47e-13 1 3.27e-16 -Time step time to completion (s): 1.21e+01 - -Time Step = 40; Order = 1 -CFL = 1.000e+00; dt = 4.853e-03; Time = 1.93731e-01 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 3.57e-02 - 1 1.15e-03 1 3.89e-16 - 2 1.68e-06 1 3.90e-16 - 3 1.66e-12 1 3.20e-16 -Time step time to completion (s): 1.73e+00 - 41 * (dt = 4.852e-03, new = 1.417e-03) Adjusting dt to hit final time. - -Time Step = 41; Order = 1 -CFL = 1.000e+00; dt = 1.417e-03; Time = 1.98583e-01 - | Nonlinear | F 2-Norm | # Linear | R 2-Norm | - 0 3.56e-02 - 1 3.11e-04 1 2.95e-16 - 2 3.67e-08 1 2.73e-16 - 3 4.40e-16 1 2.86e-16 -Time step time to completion (s): 6.91e+00 - ----------------------------------------------------------------------------- -Total runtime = 1.45e+02 sec - = 2.42e+00 min - = 0.04 hr -Thu Mar 31 21:51:51 2022 -Time integration complete. -============================================================================ -``` - -Once the job completed, the numerical solution can be read from the Exodus file `incompressible_2d_channel_solution.exo` using ParaView. - -![](uploads/run-incompressible-2d-channel/temperature-profile-paraview.png)*Visualization of the temperature profile predicted by VertexCFD with ParaView.* diff --git a/saveDocs/run-vertexcfd/run-vertexcfd.md b/saveDocs/run-vertexcfd/run-vertexcfd.md deleted file mode 100644 index 145087c..0000000 --- a/saveDocs/run-vertexcfd/run-vertexcfd.md +++ /dev/null @@ -1,3 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) - -1. [Run the `incompressible_2d_channel.xml` input file.](run-incompressible-channel.md) diff --git a/saveDocs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png b/saveDocs/run-vertexcfd/uploads/run-incompressible-2d-channel/temperature-profile-paraview.png deleted file mode 100644 index 68e9c21f9a4b081d61ec1263637ee3d80110b746..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774360 zcmZU)2|QF^{64N!Dp^V;p(%Sw_TQ>_f~j%yRENzxjOk*Z+TCuj`yS=e*Bx&zW=X^E~hO+_ODlEw_CAaw#b( zxg&=U*h@*tOR`HVmM)Pb$3!x8DXA5gu2@;w9?DC3W~-ii3>f$-1@A zoa~QWzG?d^ruV?uq6e$~iQBmGv|`GGMXQd+FWX<;T=LX@_3Kj~ zH){khT(xUq*ZFmR{j#eKPDDnoxM9!cFAi^#lB&C5a_p_* z>V1D!NV#e}sFPi=@y`BlO>O_AEsxu@=Ru{^re`@nOs|%mx?Ru~)^NkdexdTfA*tHk zmZWFWH$rx_72IEB`oLPevdY)~#ewBF5*JkMU#L%7>c6V0Qg*r4`)TL9>My>n7?@Nn zC@3Fj6sp1rX19@RBDSLL`7_iSB4rEK3hs-%4QV%@Z8x$XzT`F|#~24fFec`xyg*$wSEl*Ooj zd|-Oxo%f3~wYCjzxiYM)xb26MQ3hu)S!rO2|Cfx0md0FmgADp^!YL)ZPE8KT<=-`~ z1#cWP9xc~jBNcyc^O1YE9aS$%A9TG~*tMiUcWt1XjL%^GqSNRX#e##sYHf|6$4NSr z!#_J4mamQ$UVMFVnckV`9kSI&?8Zgs%@?6J1qqw-l#iI!Meo(9{`%y*bIi++e!+ho z(+@xWP^Vm~y<>&V&&J85A-dOu|EH~x{p+GTou}*`y-kawY>$aM*+ik&E@vmV`46NO zqb=O=uxSlN>8~4Ce7v#h%g$XYgA0#1g<2V-uQ%M2irgq;x^I!Q(bMlLIntW;g5!&- z`xNdT*r&f+FX;TKMfacMGY`&SL5qJ&XC0I_dE_h|tYhR)HHVLgP zm8yBFkbZT7Y9`-Sb$NIFe%Hs%&$sAjtUDC1aY!-K@8sU43KedBhKu$nWF&~?4vAAO z`t@IAPT%lL`MLkA=@EMkp!Hb)xiruEvO%-R%SHkv3IFV6d_JX`ra z`h_R??aC;U{A*+p-AJu;uW2=T_=1hK=w5WRdZj{Nlqdzq|b^7nm z+9JG|)P8oUc$BP@zqr$KVs*$0^Jw)S>!oDP?;Bkl$*{1DU)xK9i?9ERzxhu~;Xmz5*e4#_q#g^Xy1i2#U4GYbZ_fEDzsIrZvF1|m z$oEA?{HLbVx%-}Yf?p|R3pUzzvRLq_p390qtzsm$8|9MBVl}6O&o8PN3%F_T`l_S< zoy>xAxuCv8g9U3VmYDpA-+M!0snn*8YVk|_H!gd1^WMkii(j7&o!|as+3-z!TlvSg zPM_E5T)HQE^1O1V?D~pLek(E-jZ_@;Tl41*CtGQ+j76W$sFh+&%+HleF5Ys~2)Qde zaPwo*#?5N?_OE)dd$UsA{#SQbrQM5+eWclt^to2+)e_-i)4OU`%^N3cZ3ZoW-7fpb z^5wdtR&ut1Z5!0@eX^5ZpP=8D!I@^kUc*2-BamyJr4a;+&ZMt;7(EF)5ztd@Fn zTVH=i;~FZ9M3?RTVe*gsh3zjM+3xQ=YO>E{LFh^Cex0Pl zmsY9YE%!5ivoi3W;oZ2qoO`ABnyNwutS;)^J7BP>_`$*>{+lgx9-KOQ&Z_H(+0l{% zNo8O1FBx<}mp1t#h_lw4B!#%~@ycId8S!hQ6hJE-d`;87-{2dZcjp>glVKEoQmFB#yKVb@({@ zm%-(uT^GCdoHLx%nl#EhQIs9~vvutDZ1wDVk-x}76ybAcZmG|+FSZehAt@o>=C({K zo2^ziS5Is{ka;n4dFFL9<)D(_Z6$Lh&MxJDy?eIzM3*pwBlzpC{k`UYZSl3Z*^tr9 zmWX%6Uqws>rt&z0$@&00gH6(Iox8rSSim-~SzEL0Q1*hBitjf(A9&gNer$N&^qg+X z`q{9n+VAOwl>Q5q@>(^8p1dc*#x0HC8oe4RhlUTSX(qM&lVz55?9fzvtV-;_rj9Og?E&!b>E5FozIeVq`Vym0i74w>qH$J(O zI_CG)yH9u*b^U$RV;&(RJ|!khs7j8Pb>`D*_tmee|26rg9<82ja>m@%U&Aavlb6w9 z?hcC*D=DGkAHS zDlX}ZW~;TXYWUSbWn*$tWRZ?%u6nd?0pIW{z~ zh1b;=T$$c)Q6rX_T06zCEVm4xno)I0PORgs;Bw~gB@0AFm`J-mv*w$FPmv&BvELIfC56bv1pICZvvHKEib=yksHH`{+@==Nxl{8lT zUeAkRSJe$o43;eomQ9J>u=F~eKhiv~C-%aHqzmpBeELQGb^TtIQwJTb2M@FznmusM zy7{hmqQ-%P);e(|&4we(!i>tEwUlRhCio`2IjDVzmJp@LR@04XS)phPibiS+YcmG6&-nT?R#I}d96#FTxT5c=Y#*quj8$ouOy7Unte2zJNAuTNvEx6rS)gpJ@xmw zRT*~-vc1gxVj}i4$=}Cb`_A#}cd5WROzCQf?FtymOST&F}R6XR=z+n)ZST6f?Yj>J# zZBHFO;-u|#{Mnvjmtl<^DZ$6N)K6EBQudxJKYeqn*4Fj1a((?+*)KccH(r#Tt=;Js zan81r^ER#NTA3kHB{Q?PTdAw9SA)IM)J;!X3|g-am!-{n`g{01O=xU>+G9ZXXhuyl z12so{sn(gP##2SBiXIMaA(tP+@~)H%(c&3G6hV01CnqGQY*wVO0P=Req4!|tWv9_% zzos90?ms>i4EYYN8KSOTwl))M!RYk_v9hP0uhT%mrAqq&`>|hM4YyK#NO?tNVZzEa zZfDwPmvVPH2|e0ho%Y;bwtY|Do4hl5f8WOEcssk5dW=0KdR2ST&fGpT>FW8#x4$Hm zbDN{*v-G!ZQA?zou!aI1i7Lt87I5v-V4!u2j^+RB8cVT*QX{17D)}NV))J9U?WI}KE zC(1XPFVcet%dC^BA5RjE@h#*A?j+U}*33m`DSX+zc(=MBxQf;-e^&jSnRi%N-OBMK zp|yyzmN4n_*eI0HU^@5F-M(Rx%}yj;QSa^w(GXRQC9l+`PzN&1u9;2GzA(f+<)67z zeN09I^YF5Hp)?W>LY_Be=i;}fZr8*`DW`W?eKjvVV8`q-dNo6gH9T_5ooJIDL1 z$~4Ri{iE0$cFhnBH#i+E68SQ|S$#bj;xO-9ZBY=>j+kTRrIp$2Y3!qTgs|E2D`q=A zE!3>eB4*AIS_uw&^CRtN+Sp?+^6uuvF=`-Aq;5nn(?t~9OW?Sgly?HpTIxtd4L_*- z5i$XP9{pU$@gbM>7S7hp_eMBPf0=Um9g#fW4Sj?%L{CKABdj*!D`E;Y)6)3*Jv<;A zd5(~3QfFim7q0WZ9I{7>_GQ67qVx^_mCpz@@24WAZ@rz$kzR^we<)X$zpEe>{PCnZ zv$#oW;$~Bn?x6gFeZi-Q%NM-O9Q7 zO0Qjhy3}r>9n0wSKQW=v)Kj4@eu337#eJ%FEGwj*+r8nf#`Hqg99W3T-Mg^Iw{n?; zcHDP&JmO(vBenfsCL*w)C{*XBP{>4$fuWF((fN$#-c3;(;dywr2)|IRP1kkm=-KWTO3h$KDf7U1sg8+e%< z^ws5FqNHG%-{G@?Qc`QT{Ch7rV!wG>(tqcbQ;tE7Hpk7}$UeFkE|D*~>xTLG{o9Y! z-Y_#s*2g{Q!p1NkZ{I+(Fbj?UXkjMF|68o5vGG5e1bJC#INI25v?2$%Z#2@~t-DLZ za{0!M8}|lW@-VYMaOl6xC7&!bE(Znqnd#|;hKA~f8tRe*JoWTVO-=Q78R!`p=tx@V z1cv(tT?o_h4b=S4PX4=}1MY!t0ayHju8@5<{@d?`i{#)S3k{8b2l}7uKmSknuq*#N zl5gODJ(lDF_5Q8V)7Ral_dk70O!xj*Q`2SY^e+~a*%(MSHrh&okUH^B?|5No}rhE1NefIw| z7ymiY|4}NLY0Kq%_5NqoESLX7tuT}X2!UsYKDV9EZx+(N~Bw-P$7 z?5s7zvLfPryPyjEroP&je~w*V-?HS@X~hTW=Z|UMU1E3b`N9yTr3)TD_@THx=iRG^ zZN>bNnX$(wBpNBITk*xV}y}-QB^X1bg$&nK^w})y5*#7{E zCX@WfQ8%E^jFu{E{Pfam-XzV_n@;T~{YxXZBtnvdO@~5|_|M z)rQZq%4>V~takl5@{1p*mJP{Kuc2{$hI&IY`qSY}&a#Nb`~go zR{0DfG3aZ`LvSb=#K6BmPXoeh9DwrHV9(K@~K7J*$C? zmPR>)bkv&q4&J?_K6SqSw~*n*&O2pm&9iDWoCM{4L=gqtgbndv;pZafLfq*o%G4GU z21&`FY4sYxrVIgBhM#;1PWPrW~je4r`e}s%fGbGt2f#lHj$A_1uf5ABZ8h z4nBVy-b+F0obQlpdcz^?J=$o(Cy!9wDnKW4eD65Xh4p19ebUtDDV9d%HN+izJNk}4 zqFzxHkT!(0p+wgZOdCwql5gaojIuSF3fB+8t{iws!pvm*4oonJNQ!2sNrThqHatNfO`wZpgtYJSKKkA81&r-DBl4_8qLF?ATkzF< zI{#oRD8augdkDi_I0P+=8Q2qx<(?5+{{yT#;%W*!;AcB<>tIvQ6ynjaG!j^j>jlK;-NckzT%dmEum8;HA$ zD@-XqY;X&yh1ta47jvP(QZBgK0GE+P1Y9O*eawxK8F=W`=fI%*Q)B@hL$2P;18tYr zdE~-D#XKn7Yo}4~X7C1|QQMicK^2&vhZ9}~bXPiX7bh;JO7Bbg7M8~+Ro@E69Ky6C zck<&~U>dwDY(mfY-IuTG96dKH?vxq_j@u|r5e%ki7vL+WcshCT0Q~$AKDmovG<)qkj-C$A=NEA_1gv>v7^7EzThTm^Da=DMQMcmk-gK<+%!BS8xenYX;2YtW z!DI~b7g&4~g|dF|Ua(1997`@*dVqR;gNl-&Rl)P;;KD!boI&IYM%RyUl?U&Ro}DL{ zSc%K=MtnX7>HxJ4Tab_MFAoQ=`&DHM-8KxkMW(uT>FUQSJuSylgAs4DdAxD~ z8il={V?8^HhJYj8$0m4WZkFrje`;%q3E350&@JXkZsBig2Yrq@E{YXB&4Z`Q-dXS5lN73@LZVE_T=!9b5aW_@4zFhYy zMqh@IKuN1$`)2O^4bUWFrGNk?@`HPMduKgk|KtO{9wS1GG^HLwr{+8xW7-X8!Kpqb zf8>|-=(6$v5)mB=OP;B@1genB&$}LLgb1?{wFe}FLm+k@DdJ{NOCO|~0{NbZU;1e= z*GTLs91k6#PP%9&aLDCTXc5l7K187B(jTyjx)7Uj;ZY_y2UdquK?^;(g0cFP zwxARz;F&q_9746niF``Pi?v5lRg^pjMT0ePx?CbpA>#5P@0ZvJhn$7;!YWg{y8;AM zCTR>BAk%=#WhMBECJ;CYk;^OBpTY{9Kob+*frp5vxD3-VkWi+H-*HyI+yGOq0>9@C z4Dkj~G^a=UGfWM^G?l-d5tf1!py{>Cgdp4j=s?=}ox_IrpQEtFUDXGl&GNNt%pJjV zw2ifwB9i^^8-EX`m~e!M)emq!Br|^#?I*p5oed8Sn{`h@GYm!j8Jc8=lenN!AbV(x zbUhD^3y>`_k(>(&9O7ig6ER;#JDX4Jfdd=ymrS-T>b1Ia7!*EZLrqANWRZJ=$pZ9QG#H}O_3_bR10M*2pT=h z@obPg2mOVuqTHt_wXFh5t_<*b7*Z;n8b(P|&{H0vUR7{`lzu}HaQt>0OQHLaOC!p* z9Z+C6?7D04KF$bl(?5;1Nls-Dsh5)H9A_HRgR^cH zP~E`JjI@o4yPF5|D+)O+d}`=z(g!b|0;XJU5z3|WDOxZSd1|-5>D{(=yP)dyn~bCc z%;#`}GaPJIri&X#Hp?LG+&bu^TL&zJhd#9tQFj5;MtoAlq^T`3dNVBS%U;2xh8b2DwgHct9(D?0Ye9P zzHEMZ@O$*Sr;Ae=`oIewXXVZG=6(})`NHOQ|vwNn{R19D{yd= zi@Xz%7x13|OOrweiqSg(e<@IcnS7G4tqSpIxB%26w;YR<$`X?(DlZ>d zrpdAuZT8H~yWV*3IfjS{lV+BB6dsLD5-`b;kFKrWvdyF^OKMHrke=a|HEEZs&fa5h zCT}d3uOC@pdX}d5R*n!~xlN0vWG$`McG+YBndZD?Xgf_OLzvY`w6J`#Po3_WrO9)6 z9={@qU}ZuS?P2!T6WmXI+%CM5?zW!%_`gsfl!NeLh24Z2KM_**77gZu1}>;Xi8((} z>fc3s@k&7ZD}a9`_@xWjHy3bAEPsYdRA-_XS*VI0OkDSWc z?=0Zb2j2GBISCbt0+i_AUar1sF_Jk38%Al_390#dMyRS*ZP{>|uvZHDBNx@am}NOxc`dv8t?>VAjYWe-G~_tD@E0*h8a_Q`c+WC&4Qw%F((!F^%sd+<$Lf#g1~NCH;Ri) zThP^n-9vhjK|@t0c{o{xfE@rU6qC}?egdCd_)w|QDb3_#1L7hk4esl?aUP5N+H5Ww z`JGdP*S$2P2kH?hlbRuN1$ zzPRi0;7z#b6Al&PN#`>`ksB8H7}Z8G{)o5;f3JJ^VLrY=NF?2u_ls(Rzu~W%TjSm} z+F9(`jOPkdHBaJfel2Xa_=zSy(^XRke+JJ)=^n!jAKbj(6RTVDU7QDkv7Gjcm0$2K z7w{;JDPq-R;Z;G`u_RHe$l>C7hIRy2i4kV-w{Z21E%krqCD>9#+B@>|Hxy#1VD_k5qAMS{FRRFT_J2gr^Ynvfal8K(7Tm0A&(-intgPx2yCe2 z_QqI%#RXfPmz_jjC(WAP+!^F6$iFh!uv3FVVF zW(r6m8m3LmEXMx`SdB;>eiCE&g)V}fVMXedzki_wjKqcoG3Z|FGIcQxjWyP>bsk*o zdi$q2+C2gC(9-3E{G&5*M?jErkz7O|{`M4I)Nnx)RFMv{Ckk` zZy2hF-OG;Pre6VPuNGiA1N-S|WP+SHiX$Vi^|bi<0!`eYHxE$LfL5e|vxK2RBTB(f z=In68UN!J~egjAKz zaG5_>s{ZmV$G3b#{}KOzmEB6$o|7CLtb!o|H&s6#?g5v=)gI;$swn7f%67Xu;Wd^C zVWww9&=Xh{D1ocP)?6`Pk8Yg%E`J51v`NG;Ei&Zq?vJ>E^nkowwjFQ_obWXX>85Uq zCbD!a*N>x~eQku6vjwo0qo9lV#nK~ks(Kq>RWqdrmHS4KX`hqsHs?5GL(9O8`qeC_m$HG^t-jjBHVNo6WJcR>5yB(EA`M)qfm z%yfcDi>;B1>x{9Ou%fg9B$CkN{PxrZM-TVjC!ui%Uc)Cy{gVd=r^2-ag?jjo+iu*x z9CpO!kpAZekMJGZ<|8R!cj3*+0=x~#lGMfHPjF+w?mz8tJ7g4+ip;W5D9e2E4kJDy zvMy3Ub^u*XXMOl2%pwJ4ID25`9ccnP+c@=yCcJcz(yNfP(!m^DPRB@zN`J#HOUMcl zRNajKv98%Av(s}3A*;`<1 zV0>`p!K_$#tE?*e0-2X?eJnskj$&BoRvar_y zyAPSJlrucCY7rwbSj<&%h#aQzmXo#!?wox!U9M7Y^I~n1oyOk=3VSy;8sQ`)3bZlS zH?)H#@OqyX-M1ZoR3upV%jv2O&_{l}uZL!Bm41gunFNw%|c#z*WKELQNo=~Ccd9XLew zfZv?MX`#F)7zyOPgbod*BrFVZc0zVd;>}C=~XUCCFghxUNP7YXs3vj@~ z!!VMNRt>%*nu6vldiZg{sF6_F($(!lIc`#j>qEI*ihnkrqAz1lKhdMyfs&tX7xEuz zm*Qsq2sDn&kR-b9*OF3voX>g+?!G^X_IvOL3ZL#DfE}mm@QPea7D@HrBwemVR>Dpg(hg0gV_x9) zg{(Ju@H|}oaMO-*v)&%CsyQ~}#+o8INKC85H7+}j6xL78+NN1Y>qhx5YAcs8>Z*=9 zXkF<9F=8;ZiZ9Zn$3t5$eZz~`;CT=}#U*AM7B$24sd-k6S2~|EQ{dAL+i4A57I65; z4}zGK6f}hV&<5KO1ESzJeEv%?cJg2omlU-Z69j|B9Q9%K$iZz`R^T8*lsyT@fP0F6 zk*gPQ>lz)0QRzk+Z$~3(f)y{08C) z*UUs^lz=zCq2>Ga-j(%}^I!u7x27`i_)Yv4kN~0()9E}E`$aN?) z>i+uShV2CF2}WnBh&9bm1LrXnvF47Mk6$_qU>c;J^?6KS2vQQ(E;r+W7ozMD>dY|s znyzU$>x#V|T&Lw068WqY_(+hc4N7K!q>%~C^Mh%B>TOvFxAxxeo+hpVCMKw!)mvZ$ zCjY`iIK@RgQ$7Q6?Sx7G9qEMqHcJbbXkY)_4qd)BC6YC>aDM-3}K@>c}nr4dP_9x|YU;!d%S?y+?mZ_D-x8*hXmFN6dB1)tLox=7JR17^Bc0!Z$1%QYa5W@^utThe zoqs&jl<0I|5!^|LT(WMS&~irZxAxp{F8_8(Nj+i_DXbH^MAAPpYw)zJ#_7xN;oRGr zaQ_;K(yn=f7M0O{2Oeh?^Y(Tl4P-fs307AE-+>-#>l#PE7dL$USWo*S!>w$&Gqo%2 zg?-Kn0675+&-TG~JWf8x4FsF4%oKi{Y6pdNm~|(?@Yykmo)+K2*R%f%yQ7^*9qn%b zv}>eYviN6!k#%-y7u9XnPI+K*9HxNr`ytgZM#;->-wy0BW7Wlo+q)uem%s_FJf%O9W1Ljz4zPLq11P zFwmgNZ|5VfU`SE>4qD&1;Lv{E;CwO=Ks_E3Pmc9~9+n{+-6msh6eF;7zr?;Chcu_L z3$SR$y5@exa#TrAFiQ8A_z3D4{@6?2)Mn((*@|s+h0%qvu527;=OmBV#L#{;!&`#jc+NA=-?ga`D zA>ZqFf8-)c_z|$}gylYA>XWw)8=)v;9xbi+pz+AEjMI+7F7#yVxeJ#LdJAtct|cBF z(`Y}BDtt>sj~NC01ZI{|6YJtv#mDuDn-tcPHhd17MpozS$yXfwV}v375zTviSEqq1 z2QSzMGKM!zrnFJ_G{@)YfAZUX<|k8SD?Gs15S8G=rS7_fyO&i_yKfRyiUiGYBCLhi z5?qc836Xkbi2~zUGS)gkdiULsM?H52K7T3tu-G_qlF;Ml+bKCqoA1GIu!9evi|3(9 z7S|0PKL2c;AftDbVA`|umZB0EBCANEkwA3Pi9KCERX0L1FDDp+$bO)DY0{nC`7f_n#~>FV-*H#b5BY*PpHs%f~;r@ z>J7Mh`m*YBcot#=Xna^8b13R%rvDwRe;>UlVQ?!h{B$uLowo|SP5WEFXPgzSW=gQs z31t$-Xt>wv-uh$K-Z;Z+By_3d{2CT8hX|z9?2p}i8^8%cXc1m__9Xu8(qDr|At2i18-4;$u~-`P1>0fV z>y5!CV3JMl0Dg>}rLoi_frnUq#7}!^L&HCzsbsLSc~4OU7HyR|4Q-qypO%k4hxX*c z39q_QCY#JApz5gRAv!pOXOU-+HbNE00?%T*f;=JR1b2lA`~#5_SyM-UuM7T)n|K3H z5uZ|w-$w(M`PRRyf`flhrzwG_{JGK30FmcJe+b9RSu>#_a2EbK>|25de*z_7I%t}d z=*^*h0Y>Ar0*vwW8xxd_HqAjDBC23%(O4<0-s*u-FRSRF(^4_CnQk7UYqW3d8YCycwJ2#p`>XoH7?+cQ=`W}f5-OFu@v7T5n701_G5fdqg3W4-=*>z=2J_RZIv zi4=wDil@G0FB|cUG=G}&2flmyclwkd@8bY4gDWK(*M+h?KQ2X=A4e`9 zpA5pxcS?Iq)ZrIz!0g8pV0OS7uJwP5Cx_>Kk3$<9QfsGZ4+k@*Hb1)>9QaiXJ>@d} z2D|rzP>>WDj9K-n*GzF>qr50FUSK)A_ZsG&_?`o=f%Eo6c85t1_2SBcW}hC=$%1Jj z2U&4;H+5yw`#Hk;5&0^_{6583goi$f^XB)iDf%QrAqFTldouUp%2ltBcu2snkJaM7{Si*4tuvR1 z#gQI)3l;DtF+A#?3u|M>;)X|SkTJrB$-1di2I-%C0!|1d`h`}-+s3lr619zon7IuM z(1fAY6wK?WIhO_^3``m~roA_tOOTtaXKiPx33VK6mAvn}aj%VNo{y$TP@L@^ML z#q_RHd=WS!VbIhC@=QYf3q8IlqKajMwvylD-Jih*%Ycan+QR#8%@VTE!zR_SFuh!D zp_ng$-C6WCB!#qDLa{toG9TggAVxMBYysz$W`4be>{g;n%O3`T?mil7t|h^l%Pift zjbDYc)(e^mt{=E$I!NI!Emv_1#a3e3)^$`=rluCkcrSV{0cMpg2NuE-|AxMOvv{ih z!IP{hlP}RT5{dsa<&z3Lhf)GlKt<8xkMLkpn z!eQ}GpM5<``r^nZp-w}oZtwQn@xgsa2|Pu6z%AP5r^TDMJWf4rVb5z=PCq@oz9A}| zPd-Jekfw6;uB5rbCN=W7d!jq$#nieoU2P9GWKsUiQv{iCb7%@RG$H-Vr<{>^=z8-G z(3U!vjb9eL#}u5wYo9@~AP0GpJ`)^uE8MkO8@E<&0?~yu@Ebq7>1Q?vY|rYd)TGa{ zF!Ez$*7L5N^6T{95}_efJc1wF^1B_TTU*lYhigs7kOrE|-c$kmgIlR7&W7q=$~DDR z_2+{MZZ1f{z48R`ET=SZ;5}Uhyb+N(B=IKCi_r#}^ghB=2AmM*z79OWTYZ#Ccx7r8 zxFd0Pqr~iXaMbv_OkgLehnpQ(Y`M2=2vTY&E5(L}Jupv@sBoSKJ^XBczksI_+3Yiz zO);Q0;Nx~%rcH>biqN5=5lC4UoSh_E;g9k5JBH5$+W0FljeK4PN>G)8T$<0nt9&3<0-u(Vr5Ibnkv;qAD?^{|fu<~$)8sCbT2 z{j)L8{cT-`yaz<$lTweaz zrJ_-*S^+z0yWR4&=DwJ@XOB$AHpJc%D^Z@tIMzO2+VljE7cIWODIHQ5Ak#Atf@Z;} zMMF0WpXS-H1VyTrN2@;(<&nD={@Er>@*+mbEWsbfj>S1me8bq8ul!>Ss1h8u$1|q&Q6mu7uucxb zp&{hhaoxi`V8Ndz*f-MT&=9DB*Sjo)`1A!rhe(cMOlPHoPSy6GTic~Bb7g{kI0{!2 zovJ;3nS%OkS?^b8!K+aXj}Dt(QRXKvY*WxV`hB2uI?g6D@mxNB;cqgo_iRS=7}7_Y%k+!&bnWjbX1EL5ljG=V>+jh_q??r<5H z`VHO)MrDIbo*%`(e>A3BXP@fjOw)8foI7Yhmx)_k6(RiBVA1qPiST3P@_9?VIU7gppRLC#CsT1P&A-`Ur?dDaMPG~k;9r}t0cg;#f&T26Ir-EzL6I7 zEFqxJ7)>8UW&{bzszSYXnJ=2495*_M>hX-RhzAIdU?l%6@YQdE(q(i0uR%tU_b+R8 z*L|_?Iu|)|h-*g;Ew%NY!xA)Scx%qHZ-6TDH1I-mTNy!UGop)FzKJcKT!&LP!9!4R zcG4hJGN6^;P}Wlg`}nDg3!Dn3;Ee-c2TET&6zqsX8O4|3W`A!6&?n9^iu;NL(Dax- zN*SDvilgDiLLy$-?h2k;C1>$ye3>(6#&WE>L=9>`@;i}LP7}ZQ#H;P<>wb$geN)yA z?^>TTPQJP(TND2PPZB52u~{*cq&X(MhN>=(A)$!*te$xxg5HwWKdCZS#-Wviq8#W0 zq!Kzr_@$-WmT$$MvWxWiN`OP=2^&9c@}=Qq)HCM3MDth7pQWK{Gf-<~9%{{IEu*%n z0vbl0cfgn;&PK5DXK@E<*G@4>am5^6dpr1U9Q}g68J5UFT~(}1eyVB4YY)LYhI%fN zPEFvmmVqOq)Gqfn!Wy_a7km@VRPYrVpNR7}_Xcf<2>GM0 zILn6)jB}=ZaCS&sJ{ZQ0HO=pGh*y=E^4+4W>6;9jJ=Za$14`>XnFuwI_A-**ft>HF z-BO^dmwBbJWlpeq7kC@|@!dKxA@LYVwk#ab{I&yYa2-^h`zVHtJea@{1YRoux5~5N z%<@U=W+v&t*2syepS7b+d-Ipl-s2L7Ek$4LQ0Xj2&BtS{OpW^~SnP*Vll)RpF=f3aW7OoBj_bfn#QKI$pXv{l zynw7$HD+TDLMG;rXe8c&-)_QN)xi1F-GIZ3OU9r&I;$DU2R5hdNvh~t7;RraG~dmU zDX;tp9tm0n?a5U(HstA zP3YXf)CV}}5m<`X!*s|Rg|tBy{56Xg&Eheh;nryPz`Ve7#eQ`kDz5`YIB*TqC9^~D zTxv=zil~)@M$=g%9Ma*qWRnmIEF)wbFk@VVKerCj*sk!EFBd;_f*U}+3uYcs0<$~( ze?}nq!p16~iDq4&BYXk}P;*(yPYx}#ffl;!qA`A{eDo!k%!X~E+ zE~CX~nPPgV+oIc{_wT_n)8?Q9btqYn)7Nou_5u*-H>3d0;O6rmAQNcX^ve#I&Wy+K z)F!_5bOf#B544nKq*KNW#DYT0$f zYNKdQBy;kum>3UQ`Fdt6KsFyTI}5 zHg?XhK&#*`XEL`#p=Uc%Tvli?#?tJCTZgDzL#vt>3D;_Ol`BGl)p*89yp3@GNk|i} zRDRtJ9yIQLg})*-UH&oR*uhevpSZ|sJ z>i;gPlHqNPDwr8^+)sNwxdD9leBKOs>JKVQecRfw36o_FG0F+~=ikG-t`v5_f7Mek z^#TFn0<1lFB#75oU<00J3lyUKv?G^1kg0FQ zh!GBW`V6uE@W=Z`X%!U?TuH!7H6H(zPd%AAj#9k}d0OD02D30` zyYKsY?%(sgp1-{;<27@Czt`vU{(P=74lGIY+7yjZfY{cWeYS!W&fF(?sj!U`pJbN1CgD<;1}< zl-1*zn@TM;-5wpip={grhXYQ3i;DO5GFnd**fgr=hS|n)`h_l|Hqp=cw4P}HGY)|P zn>lj&Jg4j5|7`N9v`@Of51Woz3e!r23Gq}s6?qy=WW+;J9Yf23u6@39^pogc*z(6| zg53w@{HZ*q(8OhR`*yX#)Fs~4Z0uD*Cq*56xeOVA4l;^sHLPIIyMOBVU&lphONs_I zo$?`=_p=}8Jl~Fv3U1!EiWul{_FuYX#njKhDn6qN5c3Cy;JAc>3qGHIf9X*0|9*~6 z1UT5S%UQ*obkx*HPOn3^OuhMtm&ak+qPqDS((7gUv;3|Hv0r@=&q{;h`Fj&IktKtrvKjG59b&$F#$qK-&V};o z1;(%A1%1m|Y&3EREGYS`3zFu;g{HE+7Q|3rRo#o2lO7mt+2l3cY~=8nC39cNyR3b0 zOVZIZTBdAxiphOIMg2YTw`cM&Fjpm8{h?EGT3!8^B!I|NfDPe*d+Ge$)VKHOI8$!k zY{)ZNm$};N)+^%SgrGSr*5OclA`GX?MDH=`kw;h@Jak8%(r-bWC|!wOK3j>L0qd68 zEovpC-ruRGw^+DpL@xG*GJ47f;)j%C9Ee8(LQg$lGLc-+mBU~m@xTh}0rWZw*1YsF+^P1)b)Zan2e@guGIrF_ zq}g_=x_2^2<~h(uvIN23{o{I4&FT7S$*OB3-CUXI$xTN0CY+7s#R;PBSrE^kuLPD8 zL(2uFaK3I8Y#~nAY;(O}00vUG92$Hwu1B?hs{xfkp6`zDu{l47XRY5yAqJtDNy;^z z?NJC|84c3ZEzlJ{#0D9I%u&j_^BQN`)z5K81e4Er1r&y1qbOU-DbaM7K&~ni=+6xK z#_p>^??4l*kO3DS&{h&f7ujK2Bu7j?=ls?4&!SJkSPa)fp(0;Gk;Ob8KH`Zl+4&oE z21fylTa9E$<$v#R&LWf0{Rhoq%hglg1%+Zs6{ld}vK2gXVa1vZ%s@@4ns8&v%9i0i zu%5wpUQi%gz_dWnFQ%K$*8#V`8i5@RgiYd`l7tmg=5Xlrl z>F^hLi?yrr+F)U<$_K8N+s~{fA6Tb@U;s+2AtbGg5HZuXu$4Nmq+tJsRrX(rNbLn5 zNPfM%fR1}}-Ep?UO6xX-&si)a?hXM9+$;N?=X>j{y?;U}Vc>T`^%h{X(LBaobV%R0 z9ek+#eVsH^R6I;^(}gv$tpX8CekESr4;OkB!m!Vbd0(_3M1nNZ*jgm5l8TT!MKY9tm(5!Gl zU@|Ee^Ik&v$|+$&W0$=5_yNKn48Wtyn!WrS&w?>7(d z&!KBH6XKqjzNyn`oDtm7L0YF8jWbUDMEp|F>^!HbojMJ`!s2u^@viUe=Ud=%nyPiH zc}Z9|9JBih7A=JRSF&(^1C-LY4}Z386>|@;8j=KX;nLsz<(EQapn`hZ?gN}o6u={@ zMbEpb(7aw52Y*8=)vQGCjn%T95hTcH^aV5`=B+nk7%+?aP9JNA_W_sam)9X!?5yl2 z5Ik&PJVi}!XF(2&^LSYavq`{rRj!nQZoxfn{Ts#>7_gOv_WF}?s6 zVWDm24uZIXdT{7qIXH~j)WOV@O^p`^Jyn9&#L3Cs-hSkXnX1jx^t{13z%*nGTsw(I z5pmQoH|Vdn;xonoG%T}UJt)xHM9U%d_ey?#8CVLMZ8F1at@` z8bJm^5hG$Wp@nmRm}ysi6*JHXGJ%M}M$Qz5lib56Q&f{-&`?K|Pzm-kkpit?_%DE- zEaz{IcE6rQj%n#ZvUBr7VLYh$*(11x_5&3)>g-Vhsb)lbh{|v0!)R-P6YElFP3~60 z?_xBP1P8v200UX1+KKS<_ywwyGnE2rnbl4*zSaXQ?rv2Fap9TT{UPyiFl7XIt13!6 z?Hw;={)T#l89;CR`H#+S<3D*_Jas-fTZ=8g{WE!73kK+kKNUZq=umusTG0^%s0Y*sylD>KUajADzB$GRwd( zPVm>$mctjiXeAjc*|e37((4_)Kr|tPx1asa{wGB8vfa zP!}Dg$?Qvmzsh+s!CMXkHW;8k8sU)}cNZ;tX+728w-bEzzpNPxi&TYUPlhMpVzxUA ztjKOhN8tC=`ID!cRZSMm*;+aJ4tTZ2l`Ci(U1$I|duvC)c(SYzH#*^m?+a~TgC-eF zoMN8L%OdbV=yOI2h{;h=VHI=9?;6uYSXM z(3OJ{{WDR#VyT5e>5N_znngB6yJq4@%&zMOy~=|(kKNi_eKq6y%-oB2^lMhU-CpV$ zeR&eMe&tepZ7x>vq33w!u;VQlgbLl_shU#id6ix~7pvtSFBv2K&E-$al(AV~K@I!P zo+`n=ZWv5V^=1*b4Yv&HBdP>tle(vw5>~Z zQLW?fQw5l#K!?;1*NvWtSAi9$(ai8X1H!<2X6}Nk>$5Y;;Dw((z$Elxc!Prme(q*m zTs5SQe9?BtCSkdT^RScdW9Z_Pho$-TZQf-WwSVXfGN#NafB8oXl!|((p(GU*@}s|S z#}{8y)iOA#Sl*imi_W^<{YQ>)dq~j5xaSU(0;@_|IKm=|)hM3CJ=|OB7#FJ;UsUg)F~{zCf3p`Ymcun_4y@MN@;N~@&6MTD8Ue$s9DGxrOZP|I^KsiqF1P3WIT$TVas zHS*WXHKAKRy!p`sx07B@TH02un`4n`=U=CaB~0P2IX2_m)sf&Qr+z$#hy!b9Z+<;1 z9Y|uF`x_IshT!d^f{O$dbG8_Z5zWeOfsT;~A1#!dKpU!YG2sk%5CzzEl-j*#wXq(AGCG5tY_$HG%16WpFhd($R$Yc={`3IxDJ$n>U@P0>e%g*DG4^(%(a}|sb_O^EpndkBFz5*gf;F)OmfjB`% zcCtKyzoQ^y@YXi@e7B9S13P+<8eG*n$rl$O3x1(kumK}L$4HZHO@96hK+Zv*>b zI(GR0T`(%=R&$1s21Tl>TbjxR_ec##d8G&OSwjq0ah}5G3Oa+_qe3u+{!IhmgzbT* z(8*rTYtV$y$OMiJ7xv*y|9xu%q4=vJsP2z5ZTUopI~*1X=CR1AM+;=(PZXk;P^s2T z`ITQ*zd3B(hC%uuOx0-Y2POX_FieQwF3=_$2K>}8t7z{^&8}lA>O#MgbO9c zWcc^Gc=Gq44!qh2ziBu7A9&)n|0&#T7+lCeI{`mKq^T|-R<#=nz*_}B)*b$8mgc|_ zMSL(^p{4lNMk>~Ad53ez9P?Y(_!iB+hI%nK0HzP}Nnqm+?dkxcmDtOiai-82dVn(L z<-#-Y_A*aA<&-;1nf)l2SUc%RcGN~oIdu%(s?{S(aLyL2gd+kU8#>N?gclR1 z5)TnWz}__PHNGMeuRnMG%mc z2ZhAbaS?~Y`s<@RDDqP&E!9c%Zpv};y%z`S;iuUm-2Rphzg=T4X5(7+j5C(yi(<&R z+E#_&e`;@0tOqp|?f`TDdoj)ROfeh??9omrZiAzDcXUz<;5$CDjBo*Fj>AYda;J{= z(t$*Ujf5y3N51T$-5nOhlC8C0iP!{@?HV`;Y%nXuYhqC29CRcP6{(|ZaGPdNBFG%WRgvF?7i%&sh5Azt_8OB)aFY@l zl2NBJ55pO_m))CpVvcnc(aK1i4xca?04cQ)m`bkld~RjsE#)=2BG<7 zQE?5hY-{`@tt5Ed7wRZ!JV1=b>y=1%#%m=gV&+WLj|(aU4X_O+PESk|Gv4_qknb%j zX9Yuqswwwdh@!Dfp*2Zcy=4&i%Ip>I|0rbf^QQZ!7ERyJHe*Qfq0%6gzyEHYHka9$+H+{O9M)-bI+I% zGu74+U#7rF!i0zfQa77=F&PU864Mn_zF3x8Mp~e>GE5UlR!<;JgM|59b4?+0z!Py~dG%+JOp2>nXbe%sJz@ zyKxAA*(E7w9eeI{qMk=+g}0TqCALIG$Rh*#GrbKIg zq_Dd5)>CYypM51;$4)L)uUL&;pb?S^PB5O|O=Tk?V!Dv0h^^r3aa6LM(*H$#P9kH-FgGTwK z{3WcUYUk8ZU*cY&8m|jIoPv$4e~|#6-{i@G^6jcAylbWW)^Koj-!1r)^txoAYU8YS zRLrPus?LH)3+anA{h0j3ezxuMS#WcvZ7DB6AkN&EAI9nV%2)f9baQ@Po1T3enl*M7 z!tnR|x0-dxuxHy*xT;}jPt~-NdBV$&uqS$96?vyCsa~7h;iywAqSDV+8DQD38ec;9 zP(FE({fb>X$Oi+vzNC?ffSvioD!X;qHTl+mcM-4Qad^~uj5AEdYW}8cD4`t_sfFjr z;*h^tC5O|7XeC4>ku>-LiPcwg>*(Qj7wZi}$Mi~0+)I%+*M%74NA}wP^S)CN0lG(L z3oHvYY~P2w2w64B-@?kEZhSX9ve8LgX2m*!_e8!Z7Og4o)hRR3nmh2pAiaYrO^Q_6 z#@U@u3|DPBoUHla_~kX!Eh)Z@Hk_^|{@QNUZ+DkX5 z3JLo&evb%B6;A4PU4(zk{ccKg4cUxtj^p_`|WV-wM#;#?Uo|3|KjO;mzD z(TDCl9s}PPtPm09ymg53F4v7Ej0dNKo83B!Y%yuOHM*8|%1AzkMo5M8JjNdgW9hX-h^MbFIF5@r(*AZtS2sOz4!KjYFhMrBL2<$ zxh6z_*5;$Au!Q#j!FI|L$Or0VK}bwJnVcM@Lsa_HYU_^Jr4e6t^liRY))YCD_OSFa z7;KCbJQ%XJudn!7apXa7?My7?Kty=xiW|QTN8i;(+;GjFulnAJj>LQ9k%OuWxF-Km znco2fVRZuh5t{W2mQsI74X#_smxW*b_u>k~Cj$HGXtDU#fw@%1kF~45 zt=hsN1AN)hmY6x35eorvp zPK*S;0F_8@D%0}*=QX(fn;#7KAnXt@{H!c%i7l^|#|&@7j^xiHf_v$*1Le=*N>ssn z53E)@!V`bpz39C%^S`crH%2Niei1ah)!j8bo*F*e9kyl+o})5s^AEF4?(&pjd-hn~ zSF_PdwFR^;**GJ#UHT6mxwy7_35i~Ev02}H1+9XgsO9lOTr!b3qS9?28ek-4$i3!7 zt;~S^&JRxS82xua+)mCnkN?oW8D`o9^5@3L%u)FKl!5p{W^LL zyO>CQgo;uBxW@~Kcx;Q4oO1Ph;KhIUJv{lhN8f+pX7Z1v&4K0cDKQT&vhxg?aMB%I zHq=Vek<24PG=ldnBiIIx1cts|bn_;tLB`k^ODxDBne@BFRU#ih_ri5&bn4zF8T4#+6vIi3%cTEc7oyKLX2!@Q3 zVxi7*X~Wqm|06(akwK0!6Wv0?y^8|y%Co;$>3f=xYuFsH(DV_FjOkT1TNOeIo)>)e zXZ2J_8Vw%cF$fxF?6&CO@CZMG>l%o_VL6jZLb63O;c4eI@CETD$RmXa&FQs-=F@g0 zn-T*%*Yw_?|4sby9a-v@@CcvzfwZ{0IYg6hLZVo~6e1)+5D$Gr>cQ9q&kUGG45+zX zbvt`#JwBtP-9FHS3^FxV845hL30Zld^*hlg?f#QKWr#adIY`%#zpUk-@dNnOCx^iu zigUC(cpgly^~eCMK*G946lgu+*bUS`05|Z@mLDJ*p>^NPpvY0W8(c|bD`>-c>eNSq+m4SnKfC0VkH39xde}(RvlVEH3_|-c{srNbv;Ab3Rf}kovX*l{DSR= zbmWfz2)f5qTay$ABKe$AQkbTNrxGR0R3D@E-&x!7*J{c>mYDk+bHgNBBrhBO80Q?f zd6P<{O)r|n?MhjUmsbbEHvOXsweXdH-NBGFcnBI55jq}!QhV;~e%{Syv=IB=-Ky7^ z$o+Qu3P8}_?!wuLvIOB|78k64W;KGZUfTFie>4@QK4%bUA?(B{h7IMEx3&SQTfYB7 z?_&68k}tj;?pxfQ-|Kb0CX4KCa*~05!I79@dz^b9jS!i3vpowT-*D0P*iw<)13qA$Dj>Reyj}Ynj6Q2ZWGOpkIj1)^-*2y9#SdzhEyQ{JO zZF9y}IF9NB`CjyZQyAJe@M|T%_5+)PHA~MKxA&eoIhk+o2_wJ-sO==mXA!|l!9y6> zv#jHgnvAf$>!%u%S~mWraV6{|c|*kkEC!}0rH1+A)G}Vw`|RL%vYh87g+HmmGxj>% zu^;|l6dYe>5H;V+6Rrn}<3b50Avl@gZ=n@d@$EJ+_8N=kuIHseci3#j$CPSo17z}`7GZ>xM<7=?~|*D=vypSl1$ zS&-nr-DtJ7 z*(h`d3g>v|uyQhTE}%hQIr}7QFg0vYu2-U=_36a-XzcZAs>o3oCCpPvTl0zrkJO=f z8%*tD9QPRi7{4A2ctg4TG8Mnvm}0(pAzTRGW1^xLz~6@`3?uCE(#K*2;(xR)Pz?H; zX0L`lVE@@-$g_KL;(S2^Ou;4{;9z;49Nt{2fh6pBkm#E*FJJx@4mBxGUu+R)WD zS6owiofr1q`5|gX>SuF@IA*U^lFK)y!G(_N8L9V4dnRbT_bjWIK9$8^O zx$7lik`VRmPTkip=hoCrU8>t0m1JB}?J@vvL=}i4Y4MYruOg|BYi8Pgvw!YH&=ymW zJ4dvFZvZCkekue{lZ{gNU2=SkOtR{CXXH;iDRul{K0B= z`orXnm#QXTrL$Z^HptT)q)##9pHWEUAVn6sicGZ0X>J|(h%y=zZNm1y8W0j*NQl~A zpxxRC*a&0Xq~t=dq_D%F@CU==C=O2KOWJs@B}yvZGY8(xKZos0cml^RU)49jPs|fF zsgVq1yF-ExCI}o9*llr+=vurk*3%-92>7q4q#k2mm9(g zG2yJ_EROj6=k6Ha5p>8t>n}~u}&7Vc-#~D4Gao+~Tk2nUdov=;To_)ntAqllqRn4>a z_B;dG1mfNdQBEUzWu6@~oQh{nb5&9eB4%!}!zbHC))6a^ z)ITkIMLyDH)5a5f1v%&zJ2zPQmu|l*Lw6~9bV5)IxLkk<_$?P}B!=d+Zc5K)Gy{Cp zWxj{~DXqy52})iD7 zAaoWK<)*Vc&9{+#RqpLqPsmV}mH%m$*NkCFrUuz*cBbcDkDP8ZQ_b#-$9Yvj>+K93 zwUp3V+;Jr~3s%~HmeQ7eU#M7uZ<&p~2Ez=3(G#c+ryR|T+fs|OXUqvrwGwmqUx0BH z7?&EQb*2$IN-x&9*CQ%WpvPF42_cjlpy~xS|CKuWpARi7F>DSrf;SJg#~y>8)7wbeNPfugH21737|@*ZzV zrwkWFp>A3O?_D11yf}t+*bH^>aDTTmq0p_q=9s#Xj!j4KD*KeQ<(6k$#E0hJ{A?_~ zsjGXpU%tGzhP!F;fd%suKYoxl3$Eux`2>p)@cPJgZ@)!(CoZ(Nx9Y10$9rgXR;(VY z^>By-89I;`R`0wHjNN7Eo4nQR3xzv$Octrdj|I~X$!)A0jN_=2QX<3DamD^0Xy|aM zsq}xc8{G!Z<^5}H_OFzduv%kQy#*ea#$N;#=YCs$nZOwlZ0Pk{5O&{{=>U$O5u#~M zB>@qscgM{41Q7pI!zD7M&Qs8+Ln~rxH8lWZBM|hu%)n z3D{2L>1o3->EXcJweah%)pK2m(xe_<{)g!-^s`H4Ko?`QcNXy!QhLz{$@2YaC)dLmA;2QxPi@|5PZby_?5=K!3)ssbI^+fRmd%1 z-z!m(lFe?6sd}^A_vw~1YT9nrAaZj?iT+L_)3tE+&!`Eg&TjSZhsaB)%PAQvOjmtf zrKS{qT3wt5Mg`}=f=xft7i?{~l7r5;HN#5=ui;1j>7wSsq|a#A>$XJFyBR~QzTByE zQ%`|0jPj#|Ga{tDNtMx~Q_|0`To-|*;azKdcH@^Fu{&Fzx7?sHdcdq3FH-h-+ypwn z9WDeE88eE6)2a^~D)-C022cNCnMlU@+cLn7Vsq6WY2r?EN9M}X8#|$)D<35G;NOsq zE=u+DFH-P`^E$E>`~DpLh&;HSSTH)|k^NWkdTRUZtr zRy|+U_QW|@iu8ViQE1q&W%Tsn+`>K31$vTIU2Hu53{2ot3&qS+@zl3Kb@YV%2SKE2Ru6U3 zF>qCy8Ixa72OKdU;`jgBG$YRd>>U^QueuyAIDSD4HK}5z!_W2bd^%YWP=0GD>Yf;J zhZ!i@&q`}};eow|GYN}9Hm(5m&!xYCQbVu2A&2k4t%>=t^v5&Yh{IieUKM~}|CAdp z*?fCRKgaFM#bqUAODX*ex$iRX3dGzdIvbcn<}=P6^vwf(jxHpRDjTl6E6aot1G_Py zhR%AmPfL}e?KdUg!ofU3p`#l7C+a2P4|7KL8OcC8I9GE_-)0=mClYQYU{)A)h1rWv zQ@i;ie5xN_m+zOGf%=E4?Wvn}NjZv(Ruek&>WQ5JZJk^#BXO-t+7==bYvyg~{N9Mu zs#N`iTV=u3{8x#XJy5dq|-6CYe+_dz`w^q#o9+7Fst zJu={Gj{W8Sa(^@Qsj}}VAT}_-g7nMT9<#6%*_5oAB=l{4X)^iYegO9oG4LK(h^T$mT0sY4 z9+s4Lb!^}(?v>DZr37|n1{M3M-C&*Im&lNO?Q`okACD}NZttE>U4*5>hmS)A#XMKLp-P&E zhvsAaI$n2xkfD)Op+DU38cESARIS-}++tp3_SX~Xq+yf=nPfQy5D9iNF>>pshH2%k z0&HF1D}vfAy}0F?sFNDr&Y=Q+v=g@5D%C4=-4b zzS3IQZ5magVhK&4EXS7q()G+Ho@ZH4wKEPBWW{q;caVH62GOk8Kf&kH&@lqt`yWdVnR=N4iu2ShA>0x#^=szU7C8k~AE}ck=QW00J-;b8>X_*zRtj|5bb@OWDd@3HS)`*>>UBYVMUhR_0HEuo*q zMs;o-sz<^Ouiu4OJVQFEPKTEd@+E|5tsbp>OI0I$dvz(YvVP%zeben8i(gHg(NV55 zEaBR?f4m|)vt<4$I&QOHr1sS%laEgWs^^3C8(VYIoF4^kB`&f~NHT@hBsyNF+dW(z z`rW=ayqcqI}U3V?K2@Yy<#LrwsT}D98c|@1My-jCYC5*1*AkrKmKH%q-ffo9HT)Fbf&Ydc@Pb=PjL#aL1UK z#oyn!-HfO_8Dh57zJ3oz21K!9A`LUwGSbkKb;jWFTe*xwZ>6#t|Jm`7YYxkPc8X#5 z=q3>-^q-Cw?akO*h1V$fS$Dg;=?>?4Q-M?w5iU1>eJg1eu4AdbWFUHW%J=Dbs!QKR z?Hmv&_1*-z`GF)?;U7cf#_Vq?*Oc?SweRC<2~(nzD*EcEm>zzBnDIA7A)5Upt)~lQ z>oP{F2zNN%wWPHMjx(!kiSZepXWjkw;>~LNp3u)GpT^%17V`732K*ri9RJ6)gi~B-z2|C7N0UvB5Kdz?A3GqLq|0Btd&>^nWDD3ap{E^Q3B%@H0*nUb#}JP zco3_K-BeVdj2jMZME=dzFxnWQ4QE!mL7VrWTG2&Lu6{!FB$`^&+gwuR%y95gpd|z! z_J7I$s$NU9RbAgw;%7oUB*~owwXl$1JQ7f6(;*9Va5r(V1L_-e?1nG}(Rglr+kghZ z6L;WO&Wufilxhp&9?rN_O;EUz*w-}CmnQHmaEv~MH2D8wWj{Wf=>;51FdHV8HKIzU zVx>YY3L_uoZN=J4uZ~1bc|<9cVZxM=uuE#`blS1x5OrHVRCO7!X=w0pm(ZD86ripb z8rAh!btG+;bxvn|2qn{#m2!KA`i{`^t%TB}=NvdJVLkBL$XHH^e_Xi;W+Qw?jFUr# z#=fR1>IVb&`?rR>K!zHlk;FhVPGc>y8J4my|3_appO}>QzqEypO4axY+EyGNrK_CX z*TB^jNzgEN!e&F4?!Rhlv?kubJ_}VjeBqnJ7qEhU=$z8k?vJxu&o!SV`R)%@CE<-T2+sp-oMc@~LPicQ5=4oy0DfOlJ6%Pf)_tmmx3xWpiW7749@ z4tBSWO6+8O!fv7}z4pY!bk$}`gZFlqrX2bbzl;+7;WKj6X8?EH*jh6q@_riDKO=0< z$1Q=RGzf$O-34n`3ca@8{cLKHe1^zBF^?F%u+8QMJK<&LCYe5^7RFpxFVgD;t(<{+ zQi;Ly+a~q~Hry;+xW4Rxt%IsRrMq_zeAE30DYU5ha*D@|nz)^jcvm<(8AM{eWTJLb z?A_-6HO9w?Qx~H`%wBs8zrIGKGF$uB@+_a7P$HuonLW2ipG8s|_|fUIU71b^xZwJ< zVre}ZqNb_t=>*ET0Xw8gITn>C$XJ|S?#+(|AbD@79&sC7o5Mhg8Y3&DnBV6f=T%#eWUn9M6w_ezoPKap{!1R-)=^*8|Wh z+bSa9tK8t!lQ99CqP6Os3F8DBp5Oj7+w@*4#*bWzj&{67$WE~DHNOh6oW@+apnm&} zb62+;d$ik6#RQ@Xs>cfA%KV#gsivr!6F6|`Gcg`ycu(|0tFm0BonGVu{6o9%2o5{R zxYCxa(XV;ivs4RJhFM8|0(a<7Fv*kn4RG&Fp6jolXtd}irn9)mxU3&@576B+MvEZpoo&D3aG8ZnyD#@On&spv9pjj_YO zriwBO^22lMBgSJu?c&|(Xs`av{_5{#XeRNp{3+b{kLEn5Z~;0{+h!aKgw&_o?*GO( zDz<~6XAMV{YLtz-Y{N$nGVjIIi^fajY@+&Hdq`p=H<#>EiyWhE*-5S7cRYPBit^e* z9EGpw7D4-o4B%~NpbH!0w)Ejoq2a4fPfK?kU%&hnF>S|%d7&Ohu$1EA3K1kXeu6Qs z$FLnOJ+i0}W<>#V_1^~zujMA|VSmz(UCXrSObfQJHB@~O%?{AY}q00Ylkr^K`!ITHcXXB89 z^O}@t9*u3sNhq@HKiz7m9{VkmMl+rK*Mu!+|Dv8w8O> zO?q1L`hsoue{H+}4aT07LO(7XxB!%hrIxaL&ZSn3;FuHp9cxj^vA~=#X;`_L?}1^Y zEw#JpBDU{;tqKwS4Aa0mlX623pf_)P9LZJlcKmHuI&X-kD02(e$?=BHKfgu9j-)TB z9+#O%;6_m;aqG^-Yf`I5`yJT~458Z1J6cpjM{9Us$~foUdT%vVeIae+-XESYoF4|$ ze#S$i-|(v@WsFOv`06tC#dqq}+M*(b$BU_Z!{4k}?Ntt(<|Cq2B1^TlAqWRzUocQ$%Hma0l_LDDjU=+lc;GL%f5LYIc*MT7d_ooF46Mr zrLo{MU(qWF0!rmt0g)sqvE@+ihe@ZPi?O=M#q-biGJa}||FRY#;&Tqm#FOkM`koH% zx9a#<)H{#y5sO0Ad2n8QVN!4Ba;MyT8XQ1sB~9dp=x$7a|2|6o`ffLEy7(C zR=@l*Ev$|Flz|#TVdYa@S%+Wbqa9kOFGuJ5C*Jd3CNTshb|{+hA=i(R_=X)@L-Th{I}% zf&~&p7N$g{+&`K+O2~x{1_rlSK_+%m;5G%O0FrVJI(|kmmsKk9&bJ?XMZ;Ae5zk$7 zrP)xd5`JcC+PSVB*M40+(#_drD#9NeZXcu67WMC$p%ILQSh1nbKAbl`1(7%J zDd}*4o?C3cztEsAgy+RIdV5+V>!JB9or_}v6;NsDllO&aqc!`~Eh|$ai3Ge_VbU<% z$LUP5J-sED*j6Tmza^o3c{PgF#pe-a^j*fUT&a4G^za5cqeK)^5|S^OVD~M8DE2@` zkcr!JCNT=#fHfe}$+z1BtL&L@ew=bVn`@a{9Er!n7S!|7NQ^zzh4`WbkmQ>LrIAm$ z>!|)vr;e(5L0j6el^NW5U9{m3ziD6c$mY0H*@;hOyE<`8g8rl7MVF+N<6{2?s3VKm z8}saq!~97)iIiV@Bv%tD274yMO-A>juGb?Zj|&10FWK~dPa7mbTOa6WT9f>K41c13Wm)O$bT zLpgU-hMBjVSLY#b5>kE;ag4)Q^GX?fz^GH0egC&jkdgz_@IEcQ-&Jj|wNiTmrv^7K z{UlwID4Yc;b5;9?y}$^vR-g%yDLIN=+S_{km~rRMA*wotS`zvroU)$R9bU_0 zwc=d@PYFvYujpaZC|k!8>JmESXXKSVJIM^*EIsu#acY=G;#M+*5^`Y=Lv#4l8ra?o zh6HEU#F^wPp~NAL zq`DaHjkYQGcU(yA0`)G@i$Eeb{iGu!c!T}E$-I^e*1u$K)2H9-f`;O)Uh3)j=UOyb z1N))uxLMTb{+;A5Qt4&f@Fm%RSPiO~o>5v!m2A-M!Q~Apn{Y|@^n0v|cMQ?_lyL6M z;dY_K54k1TUUkU0Fc|r_z~(=V($?1}1MwR=I!47@i043ptX*wHQ$4x+IH1U5A-IiX zztMt*CKU5)B4wiktzyAFi&hwuFSX)>%_IMGk*#BST59uQEnW5Xr+qh_kX#iLk=Ufq zZ4sROO*!U|vsU;A}Or?ww#cQe7eY^l1=9wv_BTDyNwwfAT%8ZUmZTbrkr6y_Oj~y7M;D zDv(>In8)airnSQsD)B<7%e}J8?inH`%fhm{1#<{%r`RQab5M1TpVQ7NGm*2JqBb9X za6*B&*~}l_%sF-%J;N`71z(`P`wZ%<>+jLvbZ*gjDmbH#CLdk#e5@Dk{#_}&p2l;x zQ^>1n9NBi7i*abH-=h%PZ`4*?wl#m{rKE(>_3kCPMar^}M<(lwf`V1DJQ_0kBOxAW zN37Pe*GYPvzjD`c;1m{=dl9|PYdKZivo>VDq{j@q1Ac$}df#CL%+fYruA#ZeH={VO zi~?UASi5e+)wM~Mq0zTDcQcQ@T0WRr%`kBqjwFHwYQ50H4F}IL7B-M$UR@bvIEy4t zFZtJS+BRuZ6%&YXU2R@&7F^9RwkPXrXxLbS@$>bUmfy%*TsvcE#<~6>E1mkX;|+P% zv;Sy(Gn|wB`^fRzr=9@{5GvHJ@6vt>^6V}8e*$ISBHq6uAZ%U}}?_pGayHrFt) zTIcl~Z9Yd_p|a}n`&-ASHz?WeA?FVxwfNS}yPVfYGuHn4J;`Pj;xO_!F)+4s@9s1+ zx=KRTfPSCQp2j1G@1BX!qiGJJ`Fr%f{}AQDAezMm zlZ}ZL(=7;5#0B$R#-=Rf#c{Re1eIh;&}ghe%6P5-I*H6etV+k~t@~Pg8peFw0!Mwe zS$>9dx1E*x_n?aBk*dISz29b`0f8n$q)V_u!&~{nX#;KVG-K5%IX%RCedCvI@adj` z6&zScSx4(@@4AO*-1|?F9VV9?F*dWmYb&tsz}ZatA6`G;an;zXllhUDsN-0P;zjzK zX=qh6$12q1CDhLAH4?atW8lkF-;<%@wLKc241G^$R@IzT>1`x8i+GmVj{SRMX2Y7Pneq+8RIw1BpP?Cx0($yqGxDDL+9Z> z`IO*D+z|x1)Dr45`=^90lIfPZjA`-?xUFzdbMk7CQCjzAXNOg2;2r!)=vdkA_tm{X z2z09!vn&?QTm${qrdM(m_Se6E${>|>Jv+}PFFj6#%df&?)9uo?ko85Y0y_(dFIOY2 zJ<6ihXTG<#=TD|SJAj?gH0AP&>S!zmoS*KDg&G)(=E2X0X9K(MeN9bKMhHy3YrEUt`n#UooR z?rs?#XY^2Z=cSL6aW_p?&%SrI7d*+Lr!3fYxr&3q8z65`urR+0{W&NeufCTF&r4!p z^>b@jr+4e@=hf3T_qu)LE0#-3cczCvav7dK@HhM{AD2w;xi0K=fjU5b(gyH70#`w ze@4Lb92@Sy20pZ^IXB)uglv!&5OaHP0ObHWqk|8Ni8l(p1)^<(jC&^QKEl$zu##ds zEjlhyOtD&i4~wnJA*hPTiES>>XOOvcXaX^u0gJC(@{)-(X?5(3*~~bGk5MP6LnZCR zzDiCD*_Z5nCqaVl7fXzA?{{)PoT@*YMsO6tS)d)ZY0z9BtCyeP9z?%KL%DTzgU2xX z#q_*uGWI8;ok>hR;XN^bRMn8BBK-$$2J#mCeD|k<6XkmItd<^w`sYjF*>ev-8oZO! zt`I#}!S7uhXI2j?_kz0Woag%GDwGM`et}&`KD6I@2Ih=Yg@OWTs&c@tGs9q`dUNR^ zxbZDEn4+b`5;`aL^%WNrj)~+w58YhyG9>bnwVE5W0@X6&%e+1gPLT#Y9zu_#rRst{ zMm&mfRV1p^p(Opq#{Mv<+%p8Da4;N>o=$JVsbLqok;H-|6{~Y!$9q0?To!^m8tyQu zLdG{lWszkd#YB}yz^tYjdn_>nnPh>Qx4Gy4cFmY#PoS2t>aj6UACq|*Kp~2?7ZU-d zm@_N*6lS316=D+A2&@IEWMAHTco%$~K01cQet4?M!X>>R%YciyUsf?QCIRxoh^j|DJUSmY1Z)j$V*-f zFs~WQOK1Ixm$gPdA~_81T6R&7QQg*G^Ts_`^>6J?N!n^jbM3AJL%M`^%mXXaTSpyl zq$X1YXCW2dL zm%1!TaA3D+C1EbhAzqk1`wc_+GH;l!3Ob6*as{kvdAdM@(qJEh_%6R;Cz(xfxu5e~ zV02Q=_4mXyi5`&Yts`+~d_Z_z`^02paRQ}1R20PG%gYhDPUyC~x!Kd98@3vzW!6oO z1M9y@{p?05Ye63m=G8^o!b$(y-0g-gYFh3|2LrXm4iz6`nvmRxzWa@fu$)ig6YF4- zO&Gg{j8jVaVhMS@NBT6ulf+mu8|%}VXNMQ(-nLW~KHJ2d=&$o=#_`zA4JL;Jh( z)-yxe+aEFExa8P~P;V-%=4#WhH3QR^fdNKgt_C!qF;trG#~x@jcFy#X=!$+~0rBOsdI!v&`v0Nn%pYQU-#7jiNs^EX#YmerW2>|tBuQ#0 zZBm0!X;W!m4nlirGiaGsZ7R|tsgd@g71O@&>ulA`bmlzY`TX$x2hN;1+jBqnb=|Lv zND5@bdKx0DG!C&#M%21WhmP9LD~dIwKe@x-smQA695 z^GC1p?A3a9OmAn|e);`ZyLkqO^>iN}e|f%ZVvqEe=LC}!Q6GiD;pK^KKDqlRBu`vQ zdjJEs4vDILPDi_Hg{wBw&e)@LmYV0oj_-DEwQFypC0O5or@Z6}iGPZ?@ovLY)!uz~ zL;DwMEN~O}G1C2M<}o4UBS@e^XV3Hgq+=@~2Y$O-Lme`#!)}>7O*^JLEr_7m{_F0z z&qgU>EQ>UhN9RE!{Mjf;v9o;!Z-9Ri=QNQTK@tA!JAOHJyb^-{c-0mjiv^ShBU`;3 z=^JQZ5}sfG7#!@I6u2;Q>juwj(pUYb5_U~7pQ~5C_S(X9ujF>)_ZRop&ZPXEpf9p^ zoqh4@>$N=D1=2#-ng7afEN344HfWbG8tuQitzBgOgdjC^XAYdV&%R;wx(}cCzG2^l z=%pVayQx(faX>-%s;ov`P#lOUDm*V=%(@Ve$&s=De*Ocv^Xk>Bnf!PApGZ`~X41i} zXgod82c=tugBZ-A1)r}R>TjVEtvIOsg$-YV;gE3Gv6!XUGZQ3ARq;2oMkfDckwt8p zd@v<&%S{@kG%-Pj?2#}CKh220jS~LIR~3x#1oLx5W`VnI3+d0Nrz8h-mQscPu*e)L zt|&ZgYkIIYuexD6U?dUzWV*fl-{AuC;;2-SU{LG}O62k{f83Z?pDZ0iH5;UfdUV)f`{I$J8 zA{W3_N`9l5v4ABX!R%u3MfTCu56$Cp$2eeP>Wnw%cjchwY7L)jbdfEoc)5o5O7%{T z>z%Uo#-ah2stT{7^32Y&)!|0@&S}aHdMaJLF}KsreoG{uv;U@j8r2q8U$t{C=>CT} z`6EVWY0sczHRkzFqentqqCzL+cs(uHT~X=QO)HhR)t8{=W7D|)uc%TNanChv5jws@ zpS_i@orQl@a(OP@=p8!0tyVDdwdi;)M!vDJ<$TOo)d#bLxcSq2)%nV+ zWt8)qJ+g1sej`jpl=OYuR``Lg|7@q;>&=budrvPt47-6B36^r}&wF4v^%QecdMr|9 zU(!C2eQvqLrikE;sN2oD<>{${DM~#U%cSnnee6?exfmUHqNP>VT!fy90JAHSo^G1Bu4++q5lkI(vTn4*q z8`I$n+ODC`gidkma3^2#`{nAaUSXIZ2g>!>wE?S1bMR{r)~?^DvG}q45)Gs6pLp1d zf!)dWSq^jhxcWYbMbb+vP3Rmo3N69@1%HmQQ>G*#7q z$#U>c>e_N9ixJQAcIM!I@GlA)Qq4`~2p4M3>}rBi)QQO7N$ z0u8CD8T=fYJQt!?vIjKc-O0kO=Sgy={NS%~D?!oNOPCeEU3mzw(oaB7Qa#e($9Tnt ze+}y26k2TzkSvEIc(K3l@cU~QB2-kmSvOLmnQhX5Q!kp*|o=;bV8)|!#d z`H~;-^wovX@GDFOGcS%}y^zZWbCNlSsjWSWZVO3|$!UmXxfQ^1AYL=7_&uDBg#H(^ zAMbv01}YO8Xr7Om(SGYHpf%;znVh~7#t21)mZzS4fWtwZ z`}8KfM|>jIclq?2?62-O5V5Q$S5Uve4z(80eV{ov?0#O4hsdYWr&!Kj;HBM0iUR5& zVSzYWXEb_Nx!A_}`tQ z>dikcJmU(}^J-jJ?xf*!%rfrzpG&93ju7ksp8Y|`vju)4=>}SoxB%?2+;u|NDvNJD zAs3JQrbCf%i}X0D8Be_l_fYwm?dq*WZb?Ij;z+v$HD9N+(IMB@>F2V=Z*bjVrvUQ< zopcVBHst8DM^Aww?v%2kfH=5H%`xP<>A7vw2)4d;&Zi5Uot#+z4Vb2W`wg67*$wVo zyNxunq2El9!u?ALxAZuZ)x_5`r_sofY!-HUVlPrci&X+|bfZzVIn4XFO(ba$8a@Ih zzF*fxdV|K~IM6CEJrM&5;Aa!xH*vF7!zFWbCL#3bjw&9IU}%6SV}OrQ>l&bSLOYVX zfzoAE$RuLbUBv$^|J9xOC&f3R##ydeB0|9mRKElL^vZ{vP1SyU3;dIg>#wE}&Y@48 zu5fw$Xr7zmEUwOk(>XgDAr~w?i80$GB`Vf;Mw84Afk#b1mP$O8oefe6unsu?vWJ@6 z2mW>g&t=NoNOV$M;wVwB2D>N5XJ!(r*GGO6`YA*AS)IpDJoB9^at3jQqBan;?L$iB*42zXQ8L> z*rU@CL@KEOgnau0!ezr04#RC}(0Q?69$@y5J)^RVj=jHp?vMqQO4vpEhIq0!L?#Mt zo_*zhchN)eZkpTuI|kk>_hlyC(qLIOCJp9g7q$^^TSO{kd*&lSs9_h0r#3yFD)%QZ z9p+A$5Iwp<_9W@`L?Oz-k8upAfJ!$_v6=7r zy5od;j{q8r?V&j%XAXjkwWYmxz_o5qPVVC#5EJ->lhs2(R2rb+EVd#WT96+rJpA*5 zed#Rm)5*G7k2ZMLDFXNIJ0~d$Ea(^=u|b#^4rGp-%;FfN_yN2XXrIPk1(}cS&5r&* z3t$bVQbl0bno08j$}_%xm*qp&MQ#uVj{D}1jt^nSVHr+nv;Gl3Y5MT5;LT*bq2z0| zYe~Uj{7BCYE8rzbhvw-*!|{D^KB^C(`AGS7R3B5C_3vz(=yYuLzVwt(c+Otxa`~g! z;)?ZwMa$5b!*B1mM_CsOTxQDwx{Lrn#2%DW^`6*foo}ZvD(mZr-g_47*p1G?`>E#h zWu-2p$@OceUF)&bjIa{s%|~z=?5?r&W1`Zm98&IMPh!A*AL>0${}=mej{2D@ZctKW z;QKK0DU=_-{BPV-HR0WKs>RB4RKmFbjW@l-?UR9Nu0KM!-5`d6?LOaW_iw^v!=;|o z_QkAHX^>$1#9?$~w+8dh;F?@@n0P*i|I7eUQDB$C6y6W*UH=ZeP~F$Fn6XH=4o@Au zXqcQkAcL2D7xst@^BeTMuDyhY23!pOk+Y98TT##n(^;|2F!h#3!;M$Cnr?7IDHJ!@ z)&!{lc|X+9Qyc729wJndSPFVuXUV_I@yNusfJxGk+=koebZkXMYe#jzid1oh>T*s1 zC8n_|4iuQ7C9mOn?(=9C2mFx6Qi$^5lVd~GY|7+EOyIf~lQDm`%7;@~{TZWTv`K zKF@(_DY*6x9!HJmsvZxS*S%E(3gLQ2m4Z!5v-N>c`Op(K-}{ruEW+{lXw#OLa?l*rv*=C>Vr#Z{M1FP1MnLUy7f z%dv%-I+Ji*u!YBL>N&AF^0A8sUCA@3n2pKdk=<-knHm$*HlMw_gT?2Y(J3pDx)r|` zz+Fba%sCxiJb3H%(K>~Q>uSL}E2la~e@4=Tfsm_9;lF>cczf^Z88UAQnMaW(axoF4 z{X5nxpY~mM3&9~d(MMDU*d?RHmwg0v5_e7CeM(8;-v6-jB6x0pGxwDrj|_q~8D|#DZ{e&<0Q=ud31fyB1NYn5@&JQK9nGHk z1!kB;CZ=fr=F%73^6-Yv*&^KBD16vDfw!}0+-wYX2nD#o9#&`$vr9IRN^%{!9ZTvV zi5A_uW$67>Fx||+hpo` z=!ZcWA4iSUsgN7o4Ol<@OLnsLYwufp@?5K4t-;|7LW+hzWh{5wNi7a|^CsUIz3-B* zk5%X)cs2R-w`_pH~KQT9OO(Q(iEz8d{e#S zqRC?7`^*QH5BU#QO0Ub^lZFo_P%>C}$ZxWyIan4P6>lcQM4}QP z;f=e_-P2?#Q1g+|nR>eko+g(6%>39)oC?qjT#2RQOo0V2)KEVLq-Vo0DbBC3EX_9q z3xWJ+w+Awhvd6tBq=Uf>=-!7pZ*^%dm!Z(_?w@Pab`jfOp@1$^WeFZG`sig?pyt*V*DFgPLwNE<}W5>fl&bY*Rfb z`!R9!Zs=jZ_MbcXIevjRY@>tAlg1j>&XagFoo%E!JL@uu{^JC>kMbPkL>K8X(rc{# zd&GZrcs~$pmT-bb9*Et_?r?m7uGVfI;Y9>aXpO3l_$IsnhOhA45^$WZPSU8phN_+3 z(OYO=!V@gR#`sNb*<4nxtWSzUcOM=Y2=WCasg-RQ!G6s~EUewZ^IyFMlr+rMq(QKqi>+g!N(um7NXGsbQ);l~LlWDe8{vNy~q&^BtoF{eDigqdJvj}MEbw=oHmnnNYdq*4>^fLiJd_u?PGA#{YMn4SF_l?| z)_I_Y2XCWW@LO~9E`EBTeSAu%roYa`20ix~UK?Zl+giD}lNtRdITm!I+T*##-M1Qq zX3ut6HQ@H0R*&63D$ZORt3QHKlvT9;59(nGNydS+n_Wvvpia|6I~tT}a?!B`f#(^D z;D%tzb&c^2k78MU5|n4VojGBx03?DR!9PRi;|)~M#k!KYSQ`E-DtTtkjrZ;f@MLD< zAwBpt>7K4i;4R*g+b<6jM~6xf!_?>&;K9N31TMtY^7dbVucO8kU-Hw zr>8IjxBtK^FDTbgWy@=wGm8|S=QIY_lFQ3!AIF|W^;w1%nYfm2ta-$*u`?kESFz$B zmaL;|F2G%+Yp4|xSMx)uKcpRxhOQ(){~0`yX(W{F{CeUl#cLY~Jv>9LlC^3jRG~NQ z=RV4LT?GP2EVIX|9Lj~|gO9-_ntMuQkAwUr@J6nddB{P&r=*x`D!YI_0}4_o9}voA z{>D$lPIdaUw#?6j;4X&1Z)ngINQvfl41Jg1QVbBD)=plauUBAAD}K7l!S1+$#LaQ? zUd7TWq4)pjhNK3QNpAANRUpLd-ua)_K{WDOC*6%ZU7h#k-m{(h>@z@oYIS{z=GD=D)S|O&VF%#w{W*fe`Eml&Qa_zd}W2S zj~8l-Y{ECYqDUz$ta$v=6fWe~n=-J>!b@@sifqFR_WMbS?ReFcxnC0#c8hi3K3fcl zb-1z+Uu#iM05>k67yP_&^1%~;yQ|e3H}wNstrLO#BDk4BItZ*gfHY{L&p$47XEmo> zc0V)6z3FxUTPva(2`5fgl!4IT9FAK7t{D;rt879O^klb63@0`a9gYHK6uj5f3M*6+ z`+WaJ^c@otdXspg|0X)8I{@_U3ZhAdL>ddH0B<>w*?CKhJ=fD*$|PVw9FGAZ%*YXGpX!vSc`r!+e83W_d`gS~ z>7k>$0&iKhkmlrXW|PjMuc$ksp|GayJQYh9Cu5CCO(ZQ4>ahEr&6pMQMbKR&Io$Qv zw3p6_AdqzxRCj^@wsy6EFiaZMWqv4C2fn_t#b{wMah>!XSo{OwlklJSi`;=Z5at?v z_X?T$zKi5c9ankMwR95juvKzy>QBMyoN&aka%V=*z9$7BQUY9U!qhVj4}d6U<1FTn zKzBZvwr9~}OGF_oJQkKDhSo{%Sms=@csFhLZ z6J%NZX$kj@rQ`ELD%udS`OG*TKeXO@mLzeEPr^GbTX?cTsk`0&Dl~a&K(LMKRoV8R z1VbkZlHYyZ${0m#uX3}&e@~TAbGyL49qaflkYw**iVn<6@<()=k4nXmny5quYwKi2 z4hm_(J32`IBFxqGX7v?9u~|Jf3YeS_hjV^j0(69+5IRe&^OwF^r{Djk8-E(b zT8XSdEu&7t!MuPtQH|OuKA;OJOp2vJM-?|nWBQvU*1y5!xX>e?oQkWO{5Nc*=J2~m ziLfWW3>*h09OyagLiCb}d#{mNb#Wavf-UE@_lpE(o#r%MWqlFgr~@dtCkb`o9| zrO>ou4vp_*G!S?@$Ra?T{*|w(yib+d9h* zv`EDsRV^z^cKtr`;oAKRa^J0fJeRDvyA{iXmUr|?1HR~aCMNG*0pqF$xmY(q+_$B! z;tc(a7njUh4tKPonP?<>4mA6Hj#@5>u_*}D-M(NZu%^}++VlgaDn-JqS+^V(X;Z4e z10?i9f8~=HxH^ipjuejc!;oIGpD50ag6{}hed(CxE5dQJ6KsDG26_6OV(W?7-hr#= z#!v03SootuwZxM>0(HTS4-XbatL*DL61}fh=PDD6%jk^*ET%;i)MCYgSl>6{mE%ps z7s1SvjuT?u3kU5jofsPS0V9+A4CF=#OGtYlH4 z*exOV+C#CdaaFGY=>z;6O5Nu49C5$(=UcUH{2tFDDzZHu?ONXkY-E)`dd?cFP9>W5 zFXtI5z{*wcPr8?;athDXAiTd`E{)VgPHO5lDD@{Zv6ndi=m#jN7Hs_Ne*!ciD*h)~ z`yIH!b|)Dt+_Dp-Hv^T=j!|v2(9+|<<;u^Jazd>PXri9}hz@K6 z9kXqobn8)YhkozQw1|1kvEXDxAJZ*+^s3?&OSf@LhttOr$oB!XXhn8A=PY@ zhX`q3Irhv=k3D^J#Fs8ujgr*{zMit9uElG58K|Xa*>|pZ9}}n}i+ACp)~XvNiD0&c zzjg^bPP%$GYQ%gVwhIce@f`lU-Xnm0z52;LzH&tSRk`bMTg9Mjfa*bw_m<6HN0DpI9JoN9idiX*_dd7JkmZkbQet`iz|qT zU;M(lj*w5hq3M=Fq#x?w%^ic-t-RBS4@Zon9=KPwDwHXnM!Hee&Yr~SRG3n{)^6Qt zs|rG0pg}Mi9MgIClwHHDsSAVo=ce1n2uL5MS66>GQ&G15@)il)GzP1_rXd_&v>p8u zvwsw>YGx=tQ?OSian{enF`yW}DOq#VrWESNnU8Or%jsHNUsaCBgfV}bq~(vsS?C42 z?DOoVC@$yQ6JM?_JOAdj?vEKJR~jo1^ht=WmS2#!P6+w)h&$$3`S8?H|K4aq+#d=j z@`GQXYTx&M&#-2Cf0e7wdBK0ljd9p$@T*vyvrFLdY`?d)n@eu0N9o#1c|j%rMUyH` zw;Vf(BS0^Qsa#ulG!(Xef%H=4?ll~NhPQ5pn5&+&x;{8}YSb_3v73)a$aSArlG0>| ziJ8Uc@5A0?8$JK}tR@_3HJ}YSddQoE1)L41>^XlC?j*$k#xqln|1EPZ^4fkcpa0f#}Hr&T7 z`k7Qa#fi#d7dxj?9{% za0Ncu@v;}=@caTlXGj1pyM<>2H2;|Iq^LYLuP?X-d;T{qDh8$`bNl!(-w8dV6-L(8 z6AOF)1(g4lPLE&)tXAm%1W#d)kZ4JXroEV1-2Vd@=KKVU%1CSWm94k8IkP*|v!@K%3%~ z~FA`@Tj2%l~#1$))+m zyO8R=pY^yM;AHw((coa4EE^r?x7wOC^@3>r#}8O!%6ha^j8=r&t>y2R5CV!-WMQ4y zgqz@vD1M`xZwEVeTMp3MJfk@?H~GIn<7FDq7Ht+Gky(P-cy22^3%r9UCpfzs~#M>Mls_? z*}@;U$O;D2;E2`rRc6Ype-vCklowxctJ+ln>LY1zQhuOk-+tu8O~W_M4KQ&2KCOKj zbD)0MFR)Eky|J`G>?>t*LR1B3@J>%N+N4V6G4ye&Sfj`!{0vy3 zrlNq(2oa+O-N8lu8dsBH6e@ESG)@T}Y6T_$f0>kh!m|7QxZw>GlwR}sCfbFeLl1z_ z_mNP|M9uoQDM;Z&q)8ir0`*`hx(9=Vn2#Q2!iLYV{h|yULRc}$Emr5mmn=fFnWzE& zhg9ANYRNrRz^$+w`{KOO^KBjXW+D9E+PP>(p^G28rbuLtY@K?VGb>8HsrAQij{L(< z2W(WY4_MQ&T*4 z%6~*LH7=MZ?yP#-RNB5jdtGvFq7OKof56Lc!>FTSgiz)tUQ&+v=ne&zLBxvN=Fd7e9 zo$JkO{*0RAaH}j4enAb*ww#uy5y? z`n#bi3^)%oO%y!1CT}M0ACPLlF)(!~c^WF~p%bU0G0mfTTLUk0!%;H_xmxQ3mVOi? zhf!#!tedH`3WouP{QdfPJqfrwJL@Be8z*c)XVJQI=@#PL^69LZU#cW%unh^Ajo8`d zThENt$wCZ?paGH6f!*1E%~v`$NHh){n{~T$a`AS9(4^bJ+9p_`yF|i=HDN)8A22Kq z!Otfez)Kzbi%?+qSAAL7A}9!?L3=OY={6QVeyGVDqUEkSh%qA&k zvQv-MD?LB{bBZ{JIW(D&Lz^r)gt3o;B`4A!s*xlN{U zb_q{6dJ%EiNr%s^o-*=EY$I+y=s=MhNarMdxEb1y73Tg@K{PBgDTEgbt^*fyxjk|7 zA-5U${;zy6nOtnAyInG#cJYz#GTCs5pkPqw9zuJbL~9zvARFwsM%MyTOA^x!Nh z&n6Snnoi_wd4}l01z5(-) z;3nK0^TmILxVBvRLGZw*6Kz{KQ9umAUjX|y`M9TlWiHp|?*wV8L>I(j)c&DN0;s4^ z>PsL|in6mM1uT7eYrXq!Y7UuS%Ah=ef9)Yp4fIg4W&=LIRh$cpDn8D^rBoq`qg@$P ze)-h3(kmP|5!&^87L)J+_jz=PDIS1U%?fY!8ZW#rxe&s=YMtQI{J{1BV{4W9;4NRC zJN5mxue9<05+bD0H6V!@@3gBMa=G>j)ML`_H0!oa4*Er7Q=Xllh11owdjEd;bSTZo zJth-E>V%eB>V>|Jyjh^L)uZNS2Dadl&XrLw7vmo9xdA&1KyF^gpNca>pJ zdr<6_yg#4{T-Zh9N^1gC{GB>LQI3Ou_y7T%4NB3TA^i(GatyGdy}awa$o)1;-gavV zUIE@^){VQhW_&eid09nf?dB`skrhKKuo9-?gKhuHBIMu2utT&`6wfr*rHOZiU!rR3 zlAFXfo*%t9&BN9CpEC%L?%GtQpq2NZja|}Q`_=1A+cvP<+`@aLn%QIcF~DN^heiDf zJ>g}g72vda@0~jA8ZpQ>Mr=gy?C5Z{*Jr~M2ih*? zyIwh+#SNLVPYFJ>eEW_Vo+r5SDSix^P6d(aq$07|dORpS!_w1;!z{560 zYjnY@_s68#EnoA~?%cg&R2osNS9<-r>A9)MOAkU6n$uoji8g$0kEW%*erW8S6|DKu zp2L>&e}P5szL-bU#tRJ!gp|7wi}_0)qUV*KZWc7d4%tSPBOJ|u*Drl*pT1cCI1FoR zuD{K4Uaoce3gkPWU*o+;#PTO+Ie3TIsd;AExrKt-#3o=d>en9ZSs$(XkZ~ZcHO97R zX~#_GN-Z5%$_l+ct-wP(&9<>qn1i2V+2ddA^;@pcJObU$&Q=u`fUSNT?7`ymt!la$YWxxi>|R|zv^J1;*IO6AoDsM z->8YTvh$n`b`&ekqo%5jKGEU3f9MtRv-t5nT(nD_)2|yIELV>VR$AK|9)JK%{^{>$inXRgKS9SQMjltI4LR=6%I!HleFighfgRXRUJTVk_QH$RQ;KaJVZU zE7bq_zU|ir#lJ@a_MW zKOSy~+h?UIx=}7i#^;VMn1Atb3Rp}a^()O}k?AHc52=z096wg_3NkL-A`g7ALmyVg z1nhkOXo+%xuW!sQ_BqK4k!N#v>ta7)gFZ)vEALf3%}qvKHncO&#X%2_z)Rs#!Np78 znq7mTg(-v>dnDka96rufB@b#+nT5Rn{)fpHa(`BAak-|0yJEc?X*rKx&AS`-=oNTD zse4?*i)YL2%NFYqYQ<}BCtG*qPVyH#zx>tSr=}~Y4iq>S07H4$uQUuhqK@Nzh8C{B z7qk4Y#m)J5=fob?7RkF_GJ5+m*Srr4y($RNHAvZEB9T-UTrR`P$T==@)0&L|&2G|pt{>LwzppbI zb0EGI9w>8MpCuuU(eAm2eI}N#q_)oydimE0t*})OCN=q*)B@Ii_&6 zMoeiBQsXn4YI7j=)(IN^A48u7+pt%|U!2|^W3oADPYQD0J!s~3A(xRt?^wI^Qu4%x zz}1u4y{59ba-q>6axE8id^cxzOZ2zvOy2k1b-9BW|KgqjSxeO!)!etgI9IQ#%%#^; zS^I@d*j~gXt4~Ae=Sn6dPhq)eO)Xg1WB2GJhIx_R_jYXa$2KoNr4JrS=abqdV^@T~ zPC7k(qv;SfklD)%W|?I|8d97s#0YgQweYob&kRGck6AmYLZ^J~ErT=o1HEpgfNl_y zD_s%=3ZByAg7?l*JFVYS@ooi_%FV~Nr4fup@56pb(+dz8i3MU)IH|hM_R(ZMdH-rc zJ8>V^%Ce@mfXTg$U8{%+E2$iK{Xo%=vHSaD9uBSXf;amK=oEpgcdb<;EM*tVbHPnu z7d}<6MpBO^rc<87#@rX-kS>v=nZ>X=JLstj4fFOjXhQe`%QGIK$t~Ud*CsHGh?w-5 zaRDW79g9S0NuUcl>^@KLe z`A@F#x|8PJXhmF7y8DsMcSfS_!( z&mWiY(Mf4L@X;M?_&3uXSxoNaN}e>P=?L&X{6qgCji9b@M(z-h-pnBCd5jFdv zpOuMb_wPA5a`tiUJ;4vw?yBs3ET;nsemu;`W?l!L@)KMN(p6rsU<1 z_eUYIREzH&P52!3@s$Ov;ifk|_OP(uzr2EB$}FxNv(uQTYl$zz7n zaJj+hoaZf|L>vz1?mr*hK)aqAe^@8IWzTXj43a-uy@wNGUn%`QY3_n?GwC(X7tK%kE{zr(y=d4>dLtbCU=v#ob#IAHZ+7(NWGzqd z+GjVn88q<4i7Z3nzyJ|KEHYjjT<>#HT8HbI#BKXGNdg%7b_LTNHT6YB=X1k5Fgf1kh z_S@{G`zW9HVHFA@tq|y(-1NQ6UwhK6h9g=HGWeFi6_z~Mat9*+{RP5~`To#vz_PiF zrI5FnZ2r^%?W-DT^KjSvb1?nQx7vZ5bhpYTPD5Wl{4OKd2KLXWeG(8L->py$KZMP-CPfA7EMfocGQY(&(l zXQ>kTN$+vz+3n`txeH;a!qaElJ{kr)>ZE0$@L3TZF#M$ zJzI@X8>inY%Y9j~ua;g@-8u<`QE2Z07wpr+RU>Ehd;mHhv#5N>K}_SeIK1 zX~y&FB)e8vy@$VYLJrTzvU9E5r-U9p*bMQ0R)xx-YT?ic@B|UpX!Kk-!kIs=Is06| zvgMq<-iw*jX1o5bydc~z7@q5!j{bQAe4*yvQEc;Ug4H{NS$IqzfxZ~Tmw$AUg5*m@ zE}ZDUYa_a)qNoof#&V-&^?+31z-6g6Cb1R2OhC#HM7;bENZuVKVELBS_`;x~Ij#D= z5t3}hJ-N+ie3sgg2hc#W_O3hd-SXjo70oE~h1bKoHj3WQdQY5upWYElhj8$F;I6$8 z{r^VWd9P3TuOoOB6MbV4F)37bmJK54 zP?d$2nf0Zw3p7!d1vhzlFyy6Kw9>zBA0fvfsmvj?3b#>~DKFTHNU;uVKyxG=>l>+^ zBoFO4tg&e4b$vtClfI1c&^jL%PuoJ7_#%*X?G0!FS1`QAq>JX$?*NsB`<6z*6H5ih z)zV>$+Zsb@e&gyT_FDu_?Y{3$4c0i9pli84CP1m|Wq|i!kdXKj=27@YtV8TN{ztBjE1Ze{At6GQ>6~JP zQuIjKg%v8Foc^p@N&Ae}3wa!AvpSonr$)93P^m`N-F3FHotA{Xmo+AbzBsQ$_1}}x zq_3@OaibxipO>37LG|b3-b@+7fI{CDVSzh~mO_u2=~B)EzvP{t_YPK!co=Rjn&SGU zogIhE|CP6we3jrg03j9go2EFZJ?1^ZYIC<@-x}G*$wtG6OH|-eEWiW%OfAcsmqBs$ za%;-^A;$a-<9~hmxk8^e-daSd-ieGieFMXxsUxp!y~D{9E_?s>JQ$E3;7#9T)Ki7e zZX`d_p3cTR3Pt5nCZahf7OQI${qM)~22OB=8rgxI6}wN)T4Pue{7J0?p-e2W$b`qL z5l`j?5bwQ{_~;@s9qim5l;Tild4?k}9jFu^!1J3z^!7t?g1dwIDecSoxoGF=V1jyM zs=00JLbvCesmH<(EXN9YNygB|`ejV*MvZbL^HCwabQ_Va4aPkO5?j|#B+I&d?QZ&) zKe$3bBlvUizbIC@Y2CHJ+LykK=lh;hFq8w2(j&AG0SRP+a2+!M1g65QT# zztU>?oIHD@-whgkSXlJ2`H}W=LM-GC&cvrm4Uc3jbLG~D8CIDvKk!;kAO$T{7nHl^ zHWurl#NR71Zr?dEn-#Tl$QC|{`y4e(0`Di!WlrVj!|nk$=Mh9>l zULkB7G(|X`3uYy~Z?Kw$c$wDyXapmdO_+jmn6X;ufogbk>d8`hr>&~PnB<7;-wCSz zd5IsiXyE8$i*BH(>n3N|C!il_&FvZ77+v@B%m0q%F#j?jdBV)-kc_Q9hrpS?LS-Fh zTyVFkI8q#GK_j)|vG^vKIwG)*lK_I||B{_42WQzaQ>jjBrA8WVu7%~xcj(j_mjT*o zArC*+_GBE#Q@rT#g~G`;<&V0_wMC`c_ zyicpen1D}oEdSW@tGwj(0Xv~eH_n?Qp;eQTV4TmDw`6u&SKJ1ZPxcZSm=-=vJgv-9 zu8tdGDx9d6(B>|F2$5n#O!_#Nmc0w(n40;j@a9qV#Q+MdA$DBw0dIO>W6-yi&TcGc zKQ0q7(?72G&@^(g-t%vs>He6r`A6=C?;bbAKS~3EZ_XOQ^<)2;7mgoO>CwD=voHJF zo1>=fP9dUWZ<89r9QMxhZ5&4(R05=STgTjp-sN^?Qow6I>d@d$LZL|4+g8S@elSAN4{R?#}}^qFj1j)CT5=WR%E90bz)M_=m#Ud>QywL_7p-E zf4e?pk0VQxpG?7xW)=e)P9LlGaPV9ZdJ|Y!C)DTGE96S7;SmBo32kL?#efB0<&QT? zY}unzNA%cOzWl8Qx}@Msn?lb70(^2W@-N{nJ#>0V>}~jscJ#TY?P=tcaMM7yKIIjb zQaR`ypgBL4@P;34nbjX?6tnrSMtOnJ8;2`)U8FOu(#8CrIbICn|6g%z0;v< z5YMBd?0v+n9!C?(g(69rH->uV;a&JLS%w_i$@gwQ;5IjOeI+;Uh6CZ+ALAjL(3~I( zHvuR~X>ge8i5v#q(DlzGi76z)9%W%TZWqvl!B?~hJQ%I!W!CY@!fbD{ZswGI@iHe#h|QWHMhgsE|JXpJat12Rq}L1K*$bMf)BX;aSV62>*mnLDLXgH{DrIxnnFemwliGTI0^war3qdE^P|y zZ1~-tdQt}V6P-rD$5%oJ>%ew4NsuXt&N79fC*H(dfn|Ug17d=U=p;e_|3}lA#zXbK zf4ovuDv9_~OsPngX%i8XHd~q`ATtg0QL`g-P3kjz$P3a_p$^^Xla7$?(X zPJ(6{ZiDh*#xIsD`|A|P3q(z08l;;rt$<29XQx(fBk!jXN7Gw}yy>XpB?u6UE(S=} z^F`$12MXDNBR7lr!U+^~>}R0=7b`!3Z}3P}T?46xQv$;KStwPJb?k|Z!)#QZ4qRW4 z5?SW8OzELTBLEb#fOo=s=QZAhV={)31MS z8l^JUMJxOcD*NBT1p7CwOcu-AbZ0ySW&8|I{cHPcX%$`kMpb!c+$u|h_{p@VNe3k zY(Yj-$dxJrvS$$%uC9vY0yVE>_ zZ-oYBfImX-&_!Os~L2%@l-d%AZpZD7l&)+yDkY;og+Ru!HyJ*U_ z{ujZ0IP6}q&*wX^mzn8&Rtn#M?kga!rm7ZzSaW8g;d*dEv%AplYVu8sN8eugG6r`eN?R9ax9KpEKM5^=i)+B-!Goy3%n z&Y*`A5k;ZpvlXuw971|9J-tS>)Ot5co^@qCxl`Q5x`A91c?>*;qdHOb#Cw2GG_}nw zQTj6P89Hq>a2`4YdJ1E)A@I*MZfDvY2;HDGwDobAzDI%4AB9**hjUn?^x)MdXbpK| zKGT6q13gpiv5C;c7V!g^T_b(u&d`o0nS%;)d}D`zIz2e-zZdx#*|)}C6E3FFaGqyG zPar@zpeDW4RN)VBE@sW%hY`;BrXNMr<1MTV3|;iw;9Lp;N!P!GdDeEkmRLN z@yjzTKAF^U(sIzq63#H#J2Nw4`Zljt#LR}C@{O8=^dWEp1T^y6HGnWULZ?O}aCdI6 z??zsj^3d*~4&_eb0CADHh-uw@EzR0a($O*;)VRrh3V%*D`~hHqTzK6f6%7ykVmrm8|B= zwAO_m@=G<6#h-8klCL-~N*F68fAFxM%?UBXj}n7#an-zhzB#9-U-Q7-Plt2z-|ssL z%X@On_Oa8KK>9S4b({qo%MWm>3E8% zdPKF!K)(h5;&%iBf7>{8oVrhB@6$*_{>7oB5{oNqQn=qK%nKfqHfqZ~-Huf@Hdo`! z7)Q&6_+OjRW!cs-G5TLG#r$Zy{Ptg!llvR@sEteCLvy9|^I1K$_&2|9-WRp-OZ8an z?6nnHb~ztvi^WXccG)~7inFmWIXy=s>_`MrwNNK&N#J~kMXw;2Sl%2mJ%d|Vc_1#O2=lcak4d+*5oQ-B};kmJ6n=Peu!7D zL<*kqHx_U`7`)!6KniYK4a>y=mIY>Kpb3A{=h#}X%hAWRWQ1K&otM?AnvwT@b^LD| zFZUHE?5RH|wfWUwYU=eZ%(K4OjlPoAfs&dSEYNswVNyR3D<7O57t}sc*SGx8j1r@2 zihW!m-l8U%Rk>|A*!y#a={Jda0h-q29?4EFW*rj%kr(LA8R5HT_ER@LTN4T_pqzB} zoyJ2eU8Bg`Fd(?4g4S;I2qi+?ol?{fqrlPIFNA%B^7!O8oNQ zsc@m_vi&4{?aJprOZg?MoHCC`R*aw}DHUd{%%dX;%b9AcS%=j;uEACal_J}{#GDGx zpQm~^$jS^CVE%o2%8X)wNPeqdrhB;2M*BLiML$q58>*g`dw4%vj2uhUwS~Kmj;FR5OuWk>$RTOx{KwcZ+JzX*=1x|SeRbfv=)V)J`kM;d^4-_<0-Xd%C1>A){+{0EDIns>%|@t=>pLN5xwW#pDYc07EN z568)EM7Nd88}w(S&H`z*h2W&lsF814lHe!5{JwIkKmYP!&&x*5yt#udo|vIZ2SH#J z7q7y!5vj&MSswDTNnE&Vmb)oVm7;<&fwA1~cl#2fP0=F2qUG9((F1eAxaPjG(n_fs zx3A9>ZEIPvoT zT>vnrp%FvC%r#UyPX49!y-1E(wiCHt;LtxR8rC0>QtR7==ng71ra`i8aaz12L48)+ zvg+Iyld>EKdwK)75AH|jTui(Tl7QRl0lPKwF29v^rj~k6yD;i9vXS4nZ;Qkt<$c(3 z2inEmyqVtxxkL@_s(v@Ty5b!X8~Eq(Gq1t0c%VA8Wua=O?%<$MvYBs|8sB?JYbej& z{`$wNd274j*eJVD*Wu^YMi~&%`4S}*y}T}F=-5-br|;IqgOEr?tcd=4-&ga$3MH=# zo}F^bG^!grY!0{Oe*meRB1smBzKgY}dkt8!S7y## zR7n-t=%^M*hTE4v?%L=Q+_dYF_EdzjrH@fFVXh32Qj<2ji{9k$yKBBM9 z^uPH>;=(VC#x=@hYe|b;k0;0c*iNNlb8nDFkGOYO5e*xLmd>z_%FwdsK;{mv!S2Be;w`8gxG7Jjbo=a{VQH-_3jQYC-+V zA$?uoO(ELHIp>AsiD@3Dd3ywwHN4*xFg=HHXu6+XPc@bfUYpBLb840+8zPOS$d3LX1*QKWO9i+~XoFJ42?Xw0}d6W529khkPgvdIMi_8Xo4^95IdrYloHw zMRC&#%EZpLkV}irMFm?=%l^0!@qL9ZI1RS8E9d&GrcyH%1018vsFVEVH`#e#E_k2N z^%@n={*+O)G1Sv*`0!o?Qo$oV=!F`+E2XZ@gx0BYju{^PKlgVpEfgJFgo*H_rjoyElClQ_|iX^Go%ToR-N`_q7BD6BVv6)VYF8wAx3` zdY@!@qRh*k3$fRYD7Ux!y`aj_r6V+hBeVBEL@6-Xa5-@1sqxb^DRQFZSOSHgI}f z=_7K7Asc9+wEx`a!FLB-^hRr>hYfD?$KQ0+o2`wUz-AXv|ES@A`)(T1l7;VH3Ezli z63@dWwf$5PRc3o!yDHsNghp2PQ0n^9Uvcjo99YRak-jH)z5(nnl3nq_<(H6qo3wx0 zxNOIva-bIF5Xc$m{z4*$y|JJ=IYQ|$o$#J{ZBm=sD=u)$uL=07hmcK$%bVOpkG{S$ z(F_s^lNek85al-9c0$n6Ab-8Ip?NG#;=<*2ZUS^yJHYN*C+-y0frFh#PqO3weA-?< zjADu(3le{cY-AUpN&yKkZ_ZmhuXhq4-6B5@ob#HDKi_$aOTng^wb;S7d#$6>$ z@+Qmh8Y|^@&)9E}R_#%_+TIEFS8g(~zDh6unH-vDa63o z)SDJUjs8BPt6Xn1M3*)D885u<9T%I>ygIoG)_)OkxL#nbPLk0Oi6&Hu<#c zoE%~)6Fv@Z(MBX|WL`yDR6aKmr)SiN+QBHl59M>f2wEfeOwq6{n+xXnn}A=#KyNR!Y+NOxQZX^*4xApW3x~4vYETz{lBY>ecDLPFY0+>DJ%&0odJVrH~(Lg7Trt99W5@DzABs2G>uyV_VkqU8t{Msb&+c;_|QYwI#j5s<>z7%ZEB?a zldOX66)ixgT2`K}at{ct<6?i49|DGX!JC#qgctVmHYla4ZWa z%+JNLA*OY&EZ6WrDr#Y>NlCb#{3E>SmE8uS(_l8dQT>U08Uy1N$eObsCW}EjOjJEk z2k*#RN49YEPYY1+Ds_yD`vgSk?PpkIW0^=c_x_TSPQ5D&o!v#^zwzGR`6m~HPseqC z82jHyg8#B@1EyhnR_G+7kPdIs3&@|r{T#5sQl`mlK}$G1Zj;UAAx|=cBp0@rJc>yj zAoDC-vwR$2rvx_dd8xv`7FEP*PZxck0~d8Wt7N{;>3)EiCuTq^bL=WCgcg>63@cPF zDhF)>bC^Tr@R$k}?w!IppUshT->ZH%dM`~t{XVC89*5^Bbdz@yPu>)I|GR4a%$!RD zv^%9)jQF6ld|^renS9lwL~mgTUtPHZ%uf{Z+{|<5=mFdu^!$RpE4!MhTR#@cCDg!C zH1z(F=uPM?%I{SEF4f9dGEI}$>KIfAfrN46{~#)*tkLAgC-4|a4IEj&n?WHmdDg%9 z!?sM<)0)%GJgBPe?bJjg@(J!JP)NZ=b(_O^Wrx6ZaDj;ngJPQh$6TQGZ1F|BEgjj6;6Pqz~|Ov2}7PhPGSNBIck`mh#s(Tp8)BfzHx zlt&%Se*P;4(li-lXjQmwt!`JCf!-18kRcP6%1;yJEU}F3pckBH-cyui1S8(R7DVg+ z0O-J~$Gk~o?OZR0re!z&{vh)8LwjN3;&Eg_2tt5n_#gvyCLkr8fc1fe!S?w0Y#gkeee5?bo=nj|*uCf!vE5qI7g^Jo01E zg+;5b#&)=^@tk3Q;rv)*xID(w$!oEyGf&V99crtHsc zxLpP`6AkS35_{R(}=gtwPj29}@LjWCkBcwJv{7B4jI$ z6#x93yhWs+NP!GZ-+HL4@t1Sxp;nwP3w>f)pCn!_(F$&n%S|}}pZvKYYLnY<{PnA` z0UwiyV>h>&r?8`d%}}N?Sj7H$dU`eyupzp_o9(E>Qn3NYy97esZT#~E@nRrQ0O|be z0KcJ$a=Ago(ridSyK~D-}}!hdz?r3&N}nF)J!>x z-{79Y4I5FV?KjwQXI~r&O}$gypLI7fP{!hK0y0Pj^6&ATNB@^aN;|mj zk-D)X2V`N!O|s(O@hhB@B`LiHIoAa)5A>P2yi%k66e{E78(Tc%ZfzjLeF+qvuBn+4Hc55eZP%tIG4@XIew4f&MK1&+T>3{3lQ9o2x|5&j--$*e3$bN zZTEA|;SP9bB#fkGQdxtGJ5`@d4N|^2k;+(UTq=8bAocp4^#X)FUWdMyz#;h0^9)c# z{Kd>oP&Xa0?O0dgzxosV@ZemG4#`Lxm8QArx*~wRvcfSgPsDUR6Ww>y(C?*4d5~}N zt}{W!Cs2br5kUz`EC#ko&oj-iyf{Rncw^KJjqe{2D&KbZm;u}~H6~%AKP%dQ`nXva zbN|CHlfLqz8vPQ)^i)on#5Csx96u$anzHw@|D|BDJsM*_p{5f%6@k~~Py?b(;jPZ+ zJZc5&bDct0UdU)+#^ffHm3juvaS=T>5}#3WLpAH2L_$vXR2}O;bD1xxPR#Y_?5-r? zIsJ@$yBFn-&WRjKCVRDHc!fE#nvKp4yEM{sM`eB|@j9vYmydS^9y8p0;EH=hp+!tB z1p)Re|ht=F|M2kuyFv6W@CvL*zmLF^UZR*6@W~B|DSuXKKQ$#dCMY7{RxOlNC zqg;I5od6yI8e*--_?L4xJ`MjJwFwy5ZBH8MD0vGWzaEf-_iIetU>{+4+9oJ-zSb6` zXM~4qi+t2MO`%f~8s&S`xBlXonQzpCC5&-i8T(bIkHzhm;OVnySCr6@uO)1u{bI@6 zwuNyJ`tdsarGa>1D}GXDc@37?Y??h~J^#Fxf-`5(pds>UjzI$$uqJqvuV&X{2UR}k zXPb|9v~e|ntQPsDpwpcV5%O5*(#jUMOp-zRYq!(8TKo&#ZNk#B)Eu8V0UYdngx4`0 zs5K0AY`uNgr*XHGDF*qaEL_a3E{QmzXMch_N%HgbCr*Li2G)+_=jwCuHd1J{fl42b ztpg=qaTx{Iw9I1m=i4c_+8;k!!p(kE%2RU5YoXjfqpK`HUtGBvhKEEqxC)JXW_wKQ zy_GgJr!M+>&6+$5`mkui?((l87i1<(li$1``j`o})3zw91G!OSkHWnKO4f(c|Fkcp zs|j>5<-{G*7az(3Rz~xr_Y2PUt#ML!yV52(Q)V)hvqT7wPdsX2Zx^-Z5&t45nTyg z3*A1+r)3HLjk!vbAf*$g+fFA%N%2;XOLL_Ysqc*hWxf-!H;yaPE#7EX8QEXciIMJ^ z!;9s5eeKwb4TChsxXI|*xv&VWTEZbEcf{hNj3grE~tcx zoebYwBMa{swu+5k`a4Cy&TX3A$UIW2S}HEw7ott>PVtQGM8gI>^P=T*0;LfudNiW~ zB`_Z*D8>yXV3<8orr$QL!hsm9D}TR8gp#3rXD7_ZQTGH6Z{S2qg8dPv0v$0p^n}*X z{(ejQDEr$aZ8GmUPfjCYdG`*iLgmE<%s81g6z6$hkBib4spUB|8GPh#eYO-*p&z@+b`w+HMNA5iy} z^i?19`V#_|C?b0?Ge4XDPPn(I|H9mFaQ>W3%Q8`6z(YUtTk*7@F71@zrd&_bc=>5g zr%>Hsq?Y776ElSOJ>o|nKUIZcj$e12oo*};2?(fuC*T!-s9a>*0STYPDHPQ82vLGB z8^T&!_HCaXlxd&Nm*xK`4?p(%m&}?=&P1IjQ!wBUcB~vH%WZxSR_X6Tqp+82=li94 zkwU%~k#yqQ5p9?CjpV+Jp#h%FkH-?-yjd9bzg$m&kZwv69G-W9RcL3h)OWIkhwnh1 zbE&5M0xlX{C#XLy{6{yV0R6&|r!J*yIqFrP)~#P!-<~EnId4v(1}B0foR4{R1UN19q!z3UEp`=2BrFi??SsE4J^MAX zgNtSG5_sJ&hGG7dooO7zvsdgfqt1NNy}YoX#@;qh9CmYgBSEyTxsuHFTp{ zKbbL;X!pM^_gajs4v79K)&B+EpV#^2_$r$w1X6BY=KfoB>HlR%lW&Di9?9Qs=fnRN z9Y2NTK~5_l26oPdA2n1oI3Hx$YZQzqC0VyK=bgr_D$)*VY~Bl+MzZ)}<1&tup$n}n zTsb=+u=e{H7sZUkH|#%LQWyj>D;Ktfoamw)g55~sA1CB4MFuZ&9#2&3bt;N!d%OQO z2$xO>MI?GeMaK|k7v@|u>;4BQkYij|GqLqK3Af`81DBbyRqxenCJlYpWwg%(u5R&h z&w(>FsO+6_LZIx`9n6*A70{sWFW^Uj`66f7C?B^+(U>KH9E<-nKKo=Hq6)D}&NagS z$^W|ik&HNIKX3&?E+SrMcp^&z_v6n=$hm=nbD)Ga3CnN}a-Uv+{DBszXxc*MC=az> z&X(^Yo%eC0(W^ws>FeMg>pI!B=s)li87tq!cMI0(5XEJo`u6IUn6Q*QdM`@sB9o5Mq5lej8K ztAR;+>^f)wk|6}uzI%6Lv;6})Mll)F4X%HZDWga!^>9B=F%;RX;Q?_l87vivRkh>nEfhzuttXf?>iE!7WX045S9Px2D|= zIwJxSuQRtKX{?8N5T0Fi43XP4VUW0P#U8Q*Cr|?%bU9|xjykGOE;5|@i9>08-UDoN z57XkS#kh~7h7|CZ0oUt1oW|gI!q@w+)sz1sA)m`zy&@EHVOlUr+E^UyZO6dpIJz5X z)_n%J$+BP5BZ`>RX?fEy0 zPx{$w+kq`pXQ(PA@8yH-xOK%IFiqw8_W9u2#blh5;G2tCuSvWNmx(O_Q1j$KS#Y0> zQZ@1YRQs8{)9iq{69l!UTVWDM)(E@}my}orO&2GN7abKWbrsqCkGBc=86;bnU(Ea= zalDEdI(BFcdu?0=SsOaaqwmJHO8y~>-ntYokeAzNtyDRlwADer}Losl{iwM zwwLJ_ds{XR1s&zGC>x`Et6MuE{pif}xOTrNgQbK0uND99dEH+2=vkFrdcm?|Xlr4X z(dQ3lONeM$`F!d|t1+&S)`IQ&ad42>a&YB2c`itD&Sj6pVrFhaMlXqop8JTVZy0ru zyI6da*l1urMZp zEAX>a&9tOTOW;r!bA$8|nDwi6@8rmpS=Y8l2aNV}=F1+SJV`^ZMOR467e&@FU%(+2 znmDBzen1Ocf29dG7~}{FoV+b@NS=8PL@1=)BycAP8!AX1Ty6Z(@FbsEaa=@MNPSi8 zdmEOhj1X*`L#8D7j;_EX76psWB}0lOXUT`sEWSu->{R2X)y>4_V%iX=!k&5lO%Xa; z{AWST4D?iVSp+#0gdx4DF*0DkVPYBsd}P0iG@_osw!vg2&tVs++FVlPuJ%T>AZkX2 z4nzIECN(@jVBmWSmwij#a)v-%ePv%dLTh_M+N^w4{!HRw=$qFD4|W(Aodwq}rLPu$ zo+l3D!hx)YQ%F0wL1v0`iA>&OT4Ak^PXtgb{YKj!e?+*`Bj=a;Hv*grvMtq(-5%Cu z%~yhR#x6DU_0M13ETI;X^D_$mHK}@;cP?x1*j#v`Jq<-e{{jB;WyeWF@*k-&y|oN0$EA zE0t|3>$l@-^wtkUzMf{iiZ($NMuR$cmlIxd6J88`x%4IMY)u@}6M-|&QmE8?GLi_%=TFMC9&-Smsm5ocmZqshJS7U5pilfIDPX2n1rIq!$ic# z*3yYuZnss`X@qvM^xWx~TG&1dL-Qu8Zx$2{eb;<{h8z1cP$04C+=5Bb8H=u6gL!i) zK5fBYzV+;%#Q1d`hpTLNdwBnh5$RNOCn0Xb#c&MvIziRLA~4YV z#^KCGys_yvDeeo-@Cdn?ICR?hD-;T3xox+_rxlzYm(%YOXen72dLFjC;*3vW9nZQw zd*;w%)Nf(^YHMAehVompDp~$y7NV)Jw8CD^-N-P>mr~;~ydAkDqyN>0Pe6RGv`=ab za9Hhw`(~8by@M2!l1olsPeSKeNQzd=ouD%?t!nb~_lk9RETB{+#HvMDI1J)l+ zuy#|8=S~+P=SB(B;4^5yJWrq$`nheTE~dzrK5hPXXZh+Wv85uj0dH(__3`*;m5(0` zHt%3h31NS7`Z>>N`kRNT6VUdt)t`5-k=@lMLlss)k=FTpY;jZJvVKcnH8#q3P0*$- zGk9~%CC;fhBx64?M1GF$Ev+{WznCi=%DKx*KN@z|JjNV3Y>uxLCOdumnQ#!wHhua5 zCO-mIe2yw=sv7J4DV&5O1F0FYmLIMjHUOs}nl>nTwTHOln$NVLNWkctS>cpfN1TkL zZ&iZNmQGwv5t_(jYtxReqMG->+xrV2muaL#tMd4YDz!#y?)Jr9 zezdpHq|r2aX*Hr+z+ zBwZkNtncw-E9J+D&t6Ao7i7kl6u*Cy!OnGCzE*ocTv&7A5NO~0azSR4ZCN$2<<{vt zwprsxVQHLeZ~1b$`j?a-CJY)+9eqflB6*{Vw}VB@uHqxkkJu~u0{mk3FB2|vCav%i zDTZ+K0VgAm9v8`G-^_b)2uN0bzVMTzKh5_4xgI-#^Z`XEZhS0yU^`a|rwZQP z@%62ROs`SFK_sgEXmhpRS(|(uzb>9{a`HuzlFN|%-CKh()07VjsWE@a1kR(TgFi{9 zDpuQ{(gX(M30G3(fkmHLy`qZC-8W!mWboakdL*PFkhk7vQTQ4h<5zYrg6nPr{zxdM z_d)V;iN{L273ojrH=R6_)hOR`&9O6UV@dV`i@tMKm$`f_0d>yv(TMYz^1x$J$;N5~ z$IroB{qa&=$HItCS#}9)i+4Jv^Bes^ZyI&MbDz!=D6X#R?@iiRvA&raD9v>$>T51B z=eKLm9U0ty)&&e*V`tY({tCY*WzRG`1CnFS9n-Ty5pitxl(Y$Mx@`*ATGe{Mab6A_ zxnvVuM(yHH3}mF5|((~iJn@bmqAukk36OW5`P+9yX^Ggp-$2uJxnpC+KY|rC|Fs8j-t*JJ!Qa=aPr7{M zl*gp+OF$`=3*z9Epf<$jK)`%bwvp)DL|RE7n0eZYVm-H+@8IwZ&O6hmQBL{24|4U; z#VWNB7ja=W>lLCqr3jIpmG#7p$4s z$C@VY{t!SKXFNYIj=%!*ZYrUBNTe?K!BSQZm(>p0Jsa!L454{Rx|GgAv!<7}y0Phb z;8*L{q35cK3T9oN4Q+1pbe`vRgZ6`3DEz%-4n5@hfyHMd^H-br6JEjIO-I7PBXGB| zHseCaN^#I)#CO`ke+1o3lq{#HsJ1_epMS1g*< za|o~7!}k1|BvFq3vp9gG)t3lyGQ^4J|28dSbxwH;q-oEH;uE>YBW|`_gcmg$(ML2T zYcT|};+He?kJCTl<5jwbzvZPH_K=%;;?BM$?FtxrbH{w%=`MNa*gDRCl@|M$2OmuS zQ1ZgamZe75a@<(0kNs3*7>#p{ znMGh`Lcxzkku-1!R)<5N@E|5iWg|E;4|=Z3j^GqV#wM^P=Qf;17l=$Nl<&LyT+h$Dk&CJH zDrur7@1NIE-Ag0=22XuL=fK%=22Kjz3Z7@n3iLcD0O)+exwjr^grK=JXvlCNvKCv>dOeGpk96v3>eBi)Kt{cK~Q2SR8$HDsAGPAN?im{x5Fh zCgu}eKvQpdb!-6=huR6?Er%a6l^i8Mx50y-Uzk@dMJv<}S}@Adpg76woh)E+tl|MUoxe@7E2#Pnkd0`^a@qlOSX zCmZO<3D@I<%q-~~%2*hRLAuA18-^l?mp~nDg{9J^SkX^$} z&nBwl2AIu17%pqPmUGB8*yAq#!yR23KP!kLM8#5fPZ2^ul1KI%)0bC*r(9y{P!+U| zej+&Qjcy`V8}BmuT2G$NT{_c*E@5M+;Jm3Y#&?D0FJnK~cB9WY_oF* zl|1hUhpa?JJFvBE7#ji;x>FdxtttV*8uf<ywa&)kPsg{5x~KK5VTAi)0I zAc~GRTcDlzO>$*CD2+mde$t@Gjnx}PFb}?p+<$6-NB|-FDh(V`@p9djZOd0ZW_pcT ziZ`mrjJ{!~ooJ`>$)@Hn)*Xsdm<`ZH84KZtxTTtMRP8EO5z=Alkcm})4r7l&^|~t$ zItcH->rqN>hh3DuGUY$pyP5S+YWS)Hr(cFloSy@a#aoNH)}oe_ zgU}40bI8BV=_%~MGZ&7~<;(tE`EuVxFu~#X@l+$>%Hg!d)|OPxu{o8QmM<6Ty%^7G zzkPmMdhp6RlqLL3I*aG)ER&0P#?h*Yb$FtPTCYP#MW3mc)3E(&*&nNTpBXOc4{Wq^ zJ%nBY2LSJX@98p5b$)BKo%~lNC0SX^z7?8F-~Yp}@I0$Cyx;f=Ud1XcenZ%8^lqQ3!l(Si z;f#fEjRy$cOlvm1Ud&v*U2?hX+|iSq*a9$c`>_WHDQnz6#KI|Ns3h~Gg2pRv53#fg zox#c2NrO25p3GKf=N6XF%AoynAlo@7zj&qcafqSQOnCZpw{aZ53kH6fx8E-N-*0KE1-f58;xn{!qqR;|dF`uJak*;opo5VdB6A zY_9B-Op#5iJ4S8WLh@27*DzudOTKVN3)TB2DTQ9CRrtDCPY*w@OG`P!N>1^EXI>0m z=HEcAVLwE0+CJ|)K1KE=sSiGSbYM4ipR>oVJxiBsa5Sui9~|XR5RsCDt;)<7r_hg# zLJ@!TAMTfS|BPbKu~(aE8~@@4P*54D?d*Y7KGkBTwVVULF0n*Uy^g9w9ZunYbW8s& zzL997on~ys37V?vKY*kdUhJZ6?x#fSWgPhFIrxaDurt5kvRPc&E;3<@h;)5!xJap~ zmNPcgHaLv-p_0|opnnW=kk;=x?3sUJ;P=hA-@g^Ra!aMC*i$^qU71shuMt^tHqyJG zC!K#=GU=q-Q*cItWik8RblJV&L99ncrl-Z!n5g`=DGdp*5JC!PMvR z#39uNKJNCg^x|&GzW|TvYZculb!vUXMzEy!$IK&h#!_YEiBJX@x1eDk$lo#a`w&Uu zs5JMyldCw~`|#~DGlpsWiWZ4<9hqT8qz*xnkGq2x2D(?&H>rURUzLkDC4DPXPxAj) zr9-=%8NbZ}AI`^$q6qY8|Io&Bh1c3+($w<1jdpR>2=}i{qhgk^M@%><{=8n}-th3{ zf$e9hs6Y2Tm)gf{fFO}P37w2HG>58N@4J^f<~!zfPm&$8O8u3P$_Y$q1pn;&7K)?qDAUYbF9UVlQ_ z60+Z(_%=ych-*aKhVbKGJS5tRhH%V5n=fkMb#B|5TEUouz1oI}$nO(;vY(m}kq|LA zV%gLzthO}HrEU_l5Z$!u(Z)jAYQYv4?g@Uj?-0Q=C$z>N6b`$T#8_Cz%F@8ysT87j zouX~PvBnOM(2V68JaU4TnVPdN@PAhGco;KcjlM%mHY9?t4ZNRmDBB0Y?+d;Ya+GLa zYKc^x(o^!4h7rf9gfP9GAa($E2ZWREoj}f(sU+Zw`mb^*VfeJQq?*4aQnD-;Q=h^S zCbmzWoorT=wnnp)#>Ai4AWudiW@WhKBalV1;zi0$^6IdACy=+jTsbL<$m=<1}#gW4sOd~DOzSiBALM4si16aol+xfxpN1x*ZW2N z6%m0pA`fu0KR5R8&++~;dL#cvHCD(8l=;$rZ11yHK{pU}zeEvLwOwOzJ~Q1_>!b}V z3-<&%5!;2N^Ypmrc5>zmb^z7|j2AUvij(ANq=ycXmcP9^qUxcxzksG^(gbm4ycF%T z;?vevJ*FkkefRU-zD4*F5pkxmJ-Z@&aK$k?oirf=o>H|aAFy)Y`&_*@?ahpjQWty@Z6bEyun+143n=r zBbq&-rJE*CZd-U_i5IS}otYspKVtr|^{8y2@0hJ=Yh_+5<)=Lzzj79%d1ZQ-dvGo~ zbG_-tP2`XMyKdNF9~M#my`co|dfm034&8d}c_wtyuRi@7N9kG5G4J$e-2-jK_hHKs z-=V}HiLXsSGT=B*n^;8CSahL$kF8cwwzbS(_(-3Nqt`#Y+!4A}O9*E*`^ocd{11L` zJsw)`=e;`+)4uHT27Ra)8Qo`nu2}0yHC)cRE^vJNx2Pr(uT|0Ep^v3~S)_8VYOQgg zEhK9GJki7IwI;p!W4_W>5b*>35QC%a*fH?y+*QLgXgS=gZ*a-6LBehR<;GC_P0!a& zVxq?2S;lZJZ#+h20xA!)I$I;UO`4+zr)QS-J$O>+KP5nZ{@FoB>G+&X6NzW<{Tb?! zUZ;jnwS}e~(_tINLCRH2ok=Cg*ROze9rc-#C?U2% zyY_^Bl{ht`=0bLRPtxqGoMX=P<6POem|Z2qF66R|mN>(5>rN`>Wn7)wYU%RJWmWgA ziKQW9l7`ES4pr&0^}zsX5_v7HdCcG9aEWtKpx5jA^Jf_gdZcb0C^Yu;TTOrdHW^-0 zS%NUcJLl?u1!;_eLL;@z*>hI~{}7g9CAJykGESKN z#+@YW33zhyrRcumic!C??G}^IjuhC8@^X|^y|QHoN~R%NAmu1o{2UVtsn+-5>|e#A z4i9i6A@PG{%cMIasz`7m$YZ7SSMQ{Ppig((ZjlddhoSGkI5JF^Qu@1i%$y&EylWsx z!t=9-f3=Q$!7EU`i7*n*g{*&}S_7wI!QcZ+@8rj9fnP!Q>ygHK!3iy^^X$w+!M6BM z|9u=6<)F9=K4WdBfzqs?kC#Ri@WMv?EUoRtVk*{nUdy(YDXObIN!}r|6{~*J&jqkD ziBjbYw^OIJv$@TMm@_wwEF!3=yVozPt}y*#O1)WS8sBh-{Gj2u>E?Rep2%u+1J0jx z^6~*`g%42K%{x5LCsDZ+$*1Yz`iD7Fw^KQqV~wjh%r}Wr8aj&Sb%aM)i!>fEX*m;u zV)-U>jJIU$JI7OaQ?+lFa*>C12nEkSPAa>PKUFpQZtvB;MBn)*I@|EC8UIJqnYcss z{{O#}yi280lwyiXvP_hOm`YN_B-ye|NcJSzvYe2Fn6jiolO$Qkk}b<*-^sok``8)F zm@#M0{XL)Scm4hW=3>sd@B4K>pN|I#lJ=_~@EN^kt&Azsd^Sjvjj8Eg1nW)n5M1i$1M?R4>iw^tVE9$Z=gx8+V?E zfH24lf5A=WU+@pkZXQ=gO0NEYC+%RD(of)Ve$y%%+dsrf=a5q3_~8eBA&tcyOfi$U z$Z{?xjGi3!cDZ1G`L1mJ*v75cy;~n}Cq+$>Gz7CLxFVrwtGbGnd@KB@rh^%%8bh3> zM@69tOq~VnI#gOv(bc#DsG-X&Pf;{OV7(d)E7!Lrw`MNjw|Z{gGZaO^{K{qit{ErW zp-IBis+ztqE;CQ7g7w&SP=l*O6{unGc5!;u2oki`4md^U&v@%0G?KnQ(1uUizlB&# zv9Gd^V{w`?+ID~IDB|?CiEwsu2U*|F7xBme2Z?J3F3PvZ6;Y=9in{=rC_WjGQw zbGMGa6>Y2^a5?1kAL?=!6}pJ}pR=BL-&rN}3oA?=ut_K-8YD9| zG0SLO0W}4re;~4$f=K$znL@)8yc}%)jy*g|Z$WGeaKrRU02&#kgW+uhBLCM7K$|G7 z7t2002t}tO*BY2%1JX4(3#D=zq(ukwEb+E;3dmVb9Ua?m(*?9LM}xhmjdD_L zk+_As!=`C#i&B*CTEIEEoYaL!!15s`2O{D$<97IU!!{hqnOH%-il`6Y_i5DQ976af z598J;T&n75nx)|{nA!mBv2>ZR8k}1gst$qZ^|)$*LOP4ICHLjrv_=$MMR^v!)8F5!-<7E zGX2<0Nx8Ya2u09PX0OqW)G(LVs5h;v1Vl*@x=b8&p0qa-6 z27JE!pBFhl!$0QNHeJfSBh|wi?IbSH>^d`D0=If{tZdv}?5*=`4EoUNeue8l#h%or zFS)OOaJ0uUx9Hq74Tzqq|147gD^b|-)Vukp%h^@B^XFUIj$zWq^}n_z*D2@NdS?^i zXLEeGt*38wR0n@Qs^|(Ce^2?sW!lmEOq4mN+<+!gt8rHjxnyv4MfiUKa_N>o^X+;D zK05WsC)}9sJ1lV7w`J(#=Od*>A{={0jr=RO?a`|N+p*?;$MDU*2b}_#>a6k3M=Xh)^l&Z3tb5rvz zT_f?9z_*zstFR~tUMDs!)_R=(l*cGA{X0ngGOo+`9q>r&dlL{dwrhcsN?wU$d(NT-q({~2CWZg%)Jq9oZaG-Q&cF16? zKuFj3ay>t0gSpR?y7Ka`9W7se=Y<#cHNRP{+}PTf3&8XG?_-;O44T7@p<~q-F#d=j zX_fLA@rO^YgAl0e;o>s$&ctVrmjc*$g_8FPtS^Ojv!yI1mKoKbv#^fX40yuo@NWzM z(Vzi;BDYVZXQ{w}uYPZ|EolnUj<&T!xfW_~&e8(qhpbQ2uMGn=pG@2|rZJG8jy5VCqH` z;r~AiV2_ADlr`-a#DBffpp=tx_{`TvXfE%-;_@ak%!v}SL-ew7EYje2EvZy9_pxA> zc(wAv$woAP`F+=BHLLdv^~lsF1HX~@JdL=((VjkN?$iD(?y{Q@m1g23V7ZX1=P_S1 zE!uRMP|Lx6Qz||8j1>q1iFbpTb=DIUjI#&TyHI75Xw&B=VVfcC{_MPae&bxa6l)DD zex`y(Y~ss$5m_fl91(d$izG0_Ro<$gFW0FW9v;e3PHMu7#*n92Lo>f1H(Sd0wZtcr z^g2>*dS47)o-!X%l5r=hc7N+|MCNKaJSV9U=sE-+Rm&b)Z!v&^qP08_vu)$#*CdItbJ2MTJ%SoB4mnZ?z`Li_B1h@+!QX@ z7X;g2bN(P>!>UWE| z1WYBC*3EDt&>@yf!*9S!S!*j)71wBi;6PiPxfAZ|=+o&iJn; zRjF<9vaUbdE{fdpPF0!_Uqvmyqyuqi%6V1mu^E}p=Q8!&<=-H5TGv-Uf=NWgS=MdW z!I+3ylB5{mGChpcvyG3Yc}<#nFCK~Q-?M1A`lf1;Xmy@Sm%XR1+Z{c(B3^+X2dCYe zR3=AN<9|J2=MMf7xH+dZCJ8zcwouc}+by&VQ__SrmN2B_u}yE`Fic&yVn0bnKo#?V z(u@SaTbV^iX}B%V9X$FGg_MINZ@yXY__D1YWdn%ilwqCHk|_|jF_&|)U~EXpAZn3m z&+n+<)H@*5oo;9K^FwT}MXaj%qukE@l176u$NNh7Q|Ol493QPpvBygq7{oS>D2JBG zsWS^tnsz-{u$z;!3tkmk&zb8cJ1m#4MuxLsvB5`y`HK+_e}iW4RC5Eqf*)3)P@e$oUrQZ@xP4x!!Mi8L5s-Gt+0zG8F_-MYnxqE$Kx07JntNo||<<;gnoAq|fxXWxjD}JVdXo+grwv z0qG0dW)7Xc=O6vZcJ7s}eE!IrANyloKTwo=z)dKVxbFKHn&;XzWlwiKy=|yUJ*|+U zeQPkYRb4_}0>9aK^6=3-jPHI;Jw>_ot4OB8h$Y{<(ae3LvzHU4_Yf)!TIFK*#8y#V z2D99`O!D7g^e>;rUcZ0%-W|Wa*Npc(|5b2I^{Pd7@~RDUVCj%ZQzN-#J?}9y@n+050&_}VU`07qAGB!6SMW*a;nN9#x@m@A%@8P0r$SG=>R{ z6$?7ftc*#3jlNUQjw(mE8ReAm9CiB^p*($*Z1ay|>})W%?C}T_3seeLR2J9s?)x0f z7Iprjw9$q16!F5E-^G=v;SrVR_G@X#Me@)s$Uv5sLKc0FK15 z1=o6uR9IoeQ_Mty_-gncv!0JEZfvVvchA}UJ#e^I6;~V2Nx4(bBe=kQ{qShnj+@8i z4)x!UrkrQTV%6D(YNIYEb0}#m`GFIF`E?BqHqK8gcrHt$il@~h`AD$ zVU^O&if2LkaBLOfg7a75XMg<}6TeesKzZ3_p0D?&Og+wO>w)D`!{w*&%|Z%i6ZbbN z?H4%s(shT4mn71asvDf303JJJ3@1N=@T&A^vsM$=)8I{9_Jz$1*Z+hokX!GHs85Sn zx9Z6Hc^?xzM!Zxn#1Sb(+{SM+_kPqLN3V_RGv2Z5#};%{nBcwW7D2V^mS3p6PBYeC zy#J{WSM}!d`zx$19D2B53wxe)PU;u0!8F9xWQ|Fm?SLk3h5H6q{Cx3ujR}>pm)!>! z(dPZxI7fS%ugNuSeRz52wEhZL{4QOa{#)};AJY=NFFOZDf?4Tp_RzsEe@^t#PR424 zJUOCtv3$oj@w-p>I!^tG*#BW17?W#y!W@FA9KL_Yiau{)R}^*(`_(NP z1Xu8kQEAStuOPTwC;ZO}gip+PoStisV<|6$V9=VipO3 zBIoge_k8Y75Imb+j|LHbax{eKZAZ*GrASrID=a=5QA6~Pk<#F^On->DVxCe8S0K;n zIC1E&;r*j9`99o6Co`9wf>v1vV6dL5TnD5 z{wS8#%0-T0tp`Ueb_9{&Xl-+nl;QALZ@3K5X}(6p%qAIZEb$q=9dqMD^t3ske#fH2+i0Q%7wLFcI(@* z#Zy+8VT>z@OmSzrXt4|x>+1I!?2M+AQ4~>RpRY{RU!v_EUAvb3TVDK2`Wl+5gQ9M z8*-NoT~m7A0X9D^EE2L6`>FSR)a3>Q6;UHaYc6BCcLihlCU%CU@sEOg|GlMYlX6x0 z&LGa`n3Zwg8WCl6{E@xg@gAI|V`p$wM5cz&2=24lcm~e-?tj`YaD}T3thflhJj_e4 zJHCIMI4Mjj}e%llmRI`+h&$tEBtD(B+|kiVL}@>$S*<<_W}dmT*hb`br%pR z6aJ(bnFfmg=%-c8c~P!15Hn^dj~Jt}%k$^LuvfuzmD3X>Au#T30ZZgVkGXLU5O7_Q zr1N>8YKxejQ4Bkp69Qo%Gt>#z#bOf238J{Ul@PC7Ud<*VE_sVKcNF3pucYT^i;|&X zy(q#MOOW=DVsfZ10J!)s1d!fhRxxw+F^H5=?VNm<{-`)-`4I@=xLD6Xw3TFN=|AR; zsr^LAi&dc$im#)l;uTqCdtzX04sNz3xNw}Q_bV+_hePQ?{0*Ffh^Urf8S6i4l*$n# zL9wb}-vnihWw={)>J1I2$ky1^-6Y@OC2<2SQtfU7Vt((SlAOTS=gkL;B5EFd0CCRc zL5h1ex0*Uo(0W&US%L`>a{ zB7uVaar?TmOh|Oxuu03b?V+qETxDua-a5!n7Ix?HmP!|V?Oc-=wR{|35<8& zs{K;>iQ>?^c%N-3n+t+!p@utljR0dKQ#Vhh?038R;Ymc?S&?A56&s(dT{kqLNCN%k zcDL#KzxFBbPRoqVuVE@oQ&&Rlo8 zKi=;i&sUu%{RNczfJK=oodhG?o zjQ3cVV0x8S?IzU&H{Tjo>syh`^bdaCMtO|klPiW%_1(4FepA5}-^$CI-H#4FR%ldw zwQAp+6}3fb17kkFDHlMubj`6B9TL26ip62KxFnI6ey0C$g$+|GAW9_}A-9DN{R)(O zQtC(4hNkzhT{1^5ua6Wih@XANOF&LpUYZvE{_t0&tcI|KiDOw1sc9TnGV7x>^3-QS zIr)m*PTJigzk;%8jc;U&cDbbwb}M`2e%zQp%%XiUgL`yJjH8mV0962 z?X6e;s!Ua7!LG{FeG`jy_z`)NpFzVFqTd+8N=7Pm(n zrmUPSGw&(u(pNr5(oV{ZCe+**Cs3z?AATx$1~l-yK|jpLTa~JO zJ^qba^!!wJZQaF6rtg+om`4>;8-mZK2?ZAZBiudj1XHz;5XYk~)2}W%o`|!1n>qE_ zMA>%cfO!_RkwZ|16DqhWT=>tPtmL9VXtG77$t{;6D|O*8(nlY{OC3huC#n@wf(E&Q zzA3wlcHOw?em1^2Csr`S z98ZAC7<1OHHs`IRe39LRJEp;+MCE}~cO>4{iJ$*jWp7g|I&*)EeCMB~@wPge*NJlU z&XjdlLJrZNw}gpXZ8T^okUrb$2p_XU7tCs%?jQ`-d;(Y?vEzhwApR-Pvt=qaJ>+|l zeZhWZj&ReHUDEAtzxai2oilzc3r2VS_mLCK6hFH1L80q>J*c6P`nw&^uJ+rIm~hOS z=H+=56Iwvun%Dh)=}tyt1EC&r6M3$JoMDmKm^zsCOcZLS#QTf~C{%Qrt4^{CCsQ)M zgSJAv22|@a?5BG6`7z#Yh712HmLmaFHGM!sBss`yEz^6`+`Za@#}(%m#9N4)EcIm8 zFr;{EyTH?)Kgq+MRV{z(zrW+wcOD2Mmp|4^xtss7Z=+z)si|_I+ZLK0G=oa&ke4*4 zo+rfx?z7ghCOc~W(>xNJ+y)*eKYTUD;mbN$uEEnEvouz?PDx+hW)#Qj+*I^aFtfX*aNL-OLWdN>@Su0&c!I zb{gkC#li;gaN>OHg~B>puXM7Oi+NmU*(bQ}i9*lGG=hxqs@_2iNYYUS?iKG5fB)7w30JkQwY_&l(`U`R9EvHPQ2 zfesy8t!U3M61NTHv-{4rFln*se z38HfFp`G%#>@%i*k1g)LQf)a(s599v0%2S3+DAbX}X!TPe$EOwdDI5sGI4Fxk-TR`9Q^jhTYDHOv%cv~Y@^48mC^?H@9%Ra#1T$a1j5HN zDCL|G?kip_k{@DR@-FjHAQA}IiFo~9ESi^*0VT`!+eZ~s;mnUtc7g;KiNR$?qGQ2+ zb>NWI{W)Tv@|eXA+}(t6lbP0J7eO$ek-tBzbN}xsovUYZaJzQSDP$k?0MhvsR^EBX z)!!0+loLPE7^wkSmiK@wOsE8e+;KktiNclakmjGPJF00 zeQ->Jk)Pv8%)FM)%Xc=lKTtiwKI=OyG!u^C_&_Z3JI3xeB=h({YAGk(>Kr@@5_6g- zcnxl7_%vX~@m{dAxR0~OhRX9CG~Q>K=hl0e(&o?8&ld${cN^F^OpFa|zc32cx_h1e z1?@-xdY=54TXA#pQ|{}*qQAHG*U{A_DcCv5a;+OHNkgaTQB&3 zxt3Eevd=R2zkjkhYPE0vGU~~;K&LIxe0ZVpGmX+$jcz^0O)^KXT-czL!lbp!{!vDr z(wssQ7w(eP&~!ZnBSb$rn7=l?YfryRu<(M>xN#~+U;Nf$K2W_JS8Ww^WMK78Fj`)= zhWJ$Lx&FpIeU20k`k^+Iqa&C2zux-6P#_s~dXsPiQcKtx+zw&l)@$j!3Q9=v50=yeg04Bf3NznA01eDK`fQbka@`$kF3dF0$dex$f-GrX|>)IlHVjK$xLa}k&cBAX)054^;~nI^>? z`~U%)dm7xH++^^G90t2BcB`HdB|bi^Ivq1WjU1QM`mr3b5UrtI=J4jd2>2VPwHh^z z3w1C(F>-6_(oUbbicEsn{%$hK1Bo6w#iQfz$lP#oJzR6wlNL1o_E%DnFs!)w+6rK zDxR|_Z12+j!V-*m0oB6`_=NNQq;cmr7^NlNnfI+z@mTo#p;N%f2fl}YF5uWoPbIEM z)?xng!(Iw4nsI?}PgLMWkw*O%aH|Y7OK9j1U1=1)=2L_@3!{N`~Q7HS$_Xgfo^FJ9( zKKHO5r{dMX=1Vo{1$><+ z?I>7z@j3fQ9pQX9?iWa39YS{eqyeA3ETZLpl3+hLheL7?6TGX*_M_?PcBmXIPGGxC&K_MQs^>#O&c-(Z8`@1>qlf}-)~U-8IDH|= z|2_$PgVCVtCN7#oS7WInU#CgCfgOTb{rm#{FrQwz3F9}UCvoUS=-p|M)*Sf`ipinW z2tiF~(!o#ACw$5d_U}@jq)_f7FJQ$sNR+aE_$zL50Rz``qaCK$2WS@fDo|^!WVYR) zr<%wi0$*|WoSU`crQ4Y{`*Dq^ej`*lG$_~Up^PexP3>no%OP*I57$9?bW9sXqJ~X~ zHjIaU*eE4ZF&jkttN#Vl@anMU1fiag4SsW4R?0ZqD=~Q~%=$?f9piA^kt4N~#(LmA zp}}tYiW8V3PT;1Pv3grfH6g$ntz!lb2|<#=fLB)fID`TSm6tv_K$S51orZgust8$D zEMH@^mW7>K32UQju9It(KTzv1skWC9>0O~5Xcm_b&c&{{WUavZ=BIzDwvoySQ!wD$2TAej+AJ1tgv1dvG1BJ)dya zJme#n$h9t5{fw~9w>?evuSIPAadyQc=ntLV&EKFotnF&4xu-B8K5IBG-@#nE z|N2I@S_I0y+5fxr7O%n2yI&vk|GaCVQ7v&na2y)0-RF7CT}(i#=U@4$tR3C=+Vy@1 z4}bWPRQ>ZxUv#Vi$(5mKP)J*sg(ve}dFEN8Q%Bp@Ho>pqd2 z{^V-y4a8O?kEiMXs@%Wtf^0iA?~GW9s?h3tKX*#-mJe2O=KhQKM4sV}8) z?!~1LhDKsBjj!V8cQ_fAq_`8FsE02&k1Xsdfg2^!ufQdl!QsrdM0X|L8GCyR;@2}d z0|qP&?pmprwCwj|0TqFZ^ueczmibN;lm(j%dF{y(zK)O+`w!eORAhvu3SiQCUcR?wmHvHOhG z8^3vjOveMtOnK-2#zp7KqB}jrpc8s4hBefwPmpKDH~)o|-bT9kbGZ3pYc_p|u+KN& zcDV7)z7vgHH=bqdkCFBuHwBmt_=lIBE!2!p_LXw49n}Yutihd&5Bk6*=C7xLecQ9W z(tCoKu|ymKW8BV@->q7irNT8Z@k)i8HJkKQ%$Qx+E8<;JK79~`yr^$m_njsDxpr~w zALIzhp%t%*KhLDfWD5mpYY8%AddLq(y1DN-GVH0j$Hzq7u$n%9g+HsB1gQ(QnFZ( zzO3Xy-N9D`tNd};e$u7dpVG|9LDx1FGu7GB z3&^jegCqwvWJmdWTX7fi14>Bweu^9`ab`2rcrucc9{%1*^CU-i@Fa_gH>5k1@3Y~# zCOs2Ej4RdxdEj`OV_JTsU>p%2%4ZHClgYX%BT{8c}S=y+~^@VZ23f`EVII8Q{2I3Vb zHN;gFE{UViPfX5iz43@&0|?$*R&PKE3^zOsRYw*@p5nsE^J33(g4?*?HW%Q{3|f;VXK<#xn(*xH;*k=Oj&jzI_kfKCP-%s4MvAM)yB-6*toerTDB^DYA+$_d)N` zM24Mw-@C?^HQaQGb3Mg&Fo%+`5+r84F0{aRcge-a_rY#%AURI;qb<6N^9CEs*~FUp z*EM^PVa2@Ca{)41dFMc_&AQENZR$ zU`BYpR_#t7KcQE33;6H+bbTK22SuC;Pez+hyo5dn<{& zkm3?$mxn8SDD}^GGdbn-^K9s52mUv|i@*jNvt`PMB|($H8D=jR0)`&mNG)nC1scCE z5IuQu;}m5VW7P%4-&_*=r?v#!b2VCn?9XiV-r2<2y^b=ax!eveM&Y0NLY&xxVj4<_ zX^m@E2m{0N@H*EoCb&%(RJ!^cOvDbQFZ+Dki}HWNU@gH^`AczOD$V>J_F1Jt2 z{d$Ty%RNo1`MB?y154koJll8Zrc+GbmLRetA((fdAx;e;x~WO50^-h1*xU6B?p4aFRu@1*B>i6j_M~f~f11jn41Wm4n{ja|K zcM=|1`1u>7{Ira*s^wl0q{h4IT&Qg*Fcj}t!kC$cHCSypa1{k!^iF}dZ?1(Aet>hp z&W10$rOzV$0aC^o=zP-p<$r?{>-ZMytA}PVGbg7pyN^C0(rmkvte((9rC%y+L!;l zM=l~M2j2hWTxxm3xjO6@uUcj9$uRatf&^X6XWjh&SDn}=Qp=^X^Xghg=YpC0_IEJ3 z<$KljQoG~_!0K4@C)EuNzQ4IZ;6?S93-4uPPxiTPs_>%bfR}F>fgSy62LKYY0>ZbD7?*~Bb3iFZakLYBRd7188L80u|B5n zp#{E)#eac(`$|tUAgPi+i`he7(ciF`J*{2^W7q1_h zUoPJ|+YMx-B2?yHPw26U2#N3Em$ye2+cs`ktn!!HNp6t>)YhRhneE^VWp6J%iaD{v zJ1y#v$YdLvAc1UJ)%LdsV{-bK;+FV9M~YJ@w2k+E%KN=p1z!%uX$>K-L5Bl*;F@K? z3eiTkyI|095pGNA>~Y;Re@b*3lyNqcy=EK(Yhq7(0W$bVGQHNwCKt7=5s#LIE!1wa;ThM?~LnJ z*-ay5r`HumeDc_I!8e)}HeCR=&Bh<~93Tb{e6eLP1WPDth8UV#5fmTFCgkcjJi z$_u4x#t2by$&)=dkIi(MsCBPf^e9I1nZ+99VPWT*sgH${eS4H^(R*j*(A>IFtoEI( zr+>z7)sd7@M2Y$A*s8)~n|jJH$AgILj$)ukFDrCu90&J;zJ*fCX5w2&ii15PwmXyo z9y*V1ezM^Zw6yauu(etJ#glVSM3`ls>`w!f+8KIQiP0r#Aa=F^==mreC91G~m^F-k zUh#f0Zx@Vl`7}Xe;pD~$N};Q~8oCpGuffX|V93Pp9}I_zTbH1jgEXlBXN#Abj>4cz z$zsNx3G1s!5eOvfLJ(E4TN|J*mks3x_%(daPB=|Q+|Ec=;EQ9AK4WsC@i5rX3PuJYS z+;)aO?j)3-@nKQw2tX6oUO`U~&wAAY$W{j_T<~a=>5lTb<04{*r^My4eS}kpIMW`A zS()4CD{s8_iXp5glA^T|b$$DaXX>5Z<*WDs%;as9O3sv~UqshV&6U(Jzx(W_c>+Sb z`|{Jb{r-__>eG~ZvI3?Uj-D@wTp@%mZwab``ONz?!4lil#hv<^pF)(u+bbj-5H2JZ!e`l~7>^6KB ziLL8(lQ{=V)^TI2K{JBrGlhO{_+MB?rx>Xm@l`QRQOYjSJVqN&@|0Yp_y*-WY|Ktt z4Q#GJX}Q==v?HAhHAWyjF5Etvhhy#7fd`MEL3gNuf7fC9k@F_nWu7B*v6JSIsPQI+A0`nRh@Q`(6X8JW5Ou;?l(J$C)t@1Cqic;+4dDb*V;}<_ zo(9H1xdk_B+MLvLW|CBL;dL(aydL*UxWe5EK69ZOfh1JNUXvQP3}sK$3Piy?R(tui zs)_brqo@;f6^19`(vq%^Ko6M9epZiF4lz-qEJ833u zjruCnUeQk#n1zy9)cYuZMk=2|tp(BTuW9%&8f3kOsX5GYY6YG2;L}J73x5YbSiXgh zk6OwosMqx4nk-<@#g~CDp)oBU3TE=t9bS#oKIr zzv2u%-}2x^$BsafV|Df`DzVjgLvn_qQ{KQSzd23$`oG1e*wjSAfk9FRM{)qI2m@Za z?3OKC z+XOpt!qDA1(vEV}lT$~@mRBK$uhKZU_xpK%w6DzB90dci6KpGP%-Nf%Sk2RZd-&lY~1%qA#uz%6F~T8e1-$O{>^WF2Lf zk{U9ruQNq^*1#0Muj_$EUl56}=VBvs)L*QTZ6y%_6U$Q<&}6i&)ZO8dG>JaLqKFhGe z<^VzQrCn^k%jC;?%%_l#)FQA!a|BGI1B{PE+!za^-tdey#fTl9*Ln-hs2dH1{xWxDXP7-MbOf3?uUC}XWDGFAhR>oxPF8BUWZp{!}B-GrFc+Lz>Oy$_Y3L6ye z=7as=H7=4&t^OpA{x>#d_+?x;GMC&Wr94k`i^5@Y41)zlND#YlUGNNL#=^Pm>u{XG7z3@v98S9M*KtrWjSXauQ?kA^49||ky?a*zSWRdlLjt3yFOk56cU+@rHENnKdH^aIm zP6pj-SJ=#TVUGI!+jsX!tbcE+h4NyN7`$55dnS*5`<> zjQuX5^UyFJ~i!LJr-=zX&v_ySzaVJ-P9F5m4jmuMvzj# zEl~jiFpZ6C3~`v7Up0^PwS4G&xu1Cb+2MOx%{#W6%COXiTi8ZXEuSR+`^Uya&O3KtJILA*NS;BdBs5wo8c*O(x64~Mh=O>s(0*ncLzyCqxp|c8SV*Rs-gAJ zW@9$CKhku!>1<*&$l&a&w1u$+&^WFU>wsyh{IgwZB|YNX4rAN)$b{#netvh55Ubk3 zS*exd$}YLxw-9R-nevYNRg2NNUrgLr!41Ag+k0Qrtz^4}q+kaN!MzyA%!f0jEi*+_^YvC&1VAJ|r+)AX;ygsY(n@c>|@|$)#j$u!v4{>?nzUjw2A48>4600r&HNFm9@SfEsgalKdJ}U4dg|nAT=KiBbr~KJ^013D#LT>2A(%zm&3jl4Erfle%Yb} zAnQ$vE0c8{pHnj(6DpN+7k4h3mBC-<9K;r@Y3kFk;J3 zt2FDbsUHiBtK0NkaI?^TTy4lpaRvmR~cFcy^` zue!b%Xu}PX`W}pRI+y<_+r>LJWbgn#*ZZR1+9|=Uw~+P~;VCi9+5Xy{Z#q~ni~I;m zEA;vNsrJSve*BgGa)jY9xJfK?0-Tq5$0Pfbh$@zw#J@c&u`d_629sR{mFJ4AGVJoE zWuW+^bF8q5A*rK2+Vzg-SrH>faM3;-444 zNU(e`iR<&tR&MxN@_}#?QS2iLJPw0!I=&Cu6#s6_Rd8Kf!3eRwDNxJYsexv}lE2jD zm1q8P`Yl|YRYOdUh@0;yx1U=ht03o*m|hAEH+l!07abQ|$L?&@KNcGQ6ja{0cnZ-v zEAV)KNxiwxnDLP2t#gzLq^NR{*k-R#c|t}&nxEgfl)j5%V6-Xc%n_KTrp0&Sal-|V zH*vkteI4|AP;Gy%#DZy0>O@*|Q*V6#D^e`v!UQu_n|-MpQk#?|Z$_@#epRFHj}Ncv zgiO6aQaq<#PsVcaOuzUI8~^#}*-qG#TCQPK__Rb3G-@CI`XXPzw6#Ve89#f|dZuvV z3hF)I9UU~W8zo%VkDs-y4b)%{hHd&d-dZ>&was++&3-El75!JPxk}0J9v=9#g`=V!-uj91xJ_tG50xGz z-%hKiG_@(ln4O2(!6BcgJ>%wen*m%PhcoM4-Od>PcaKauPyU^Mj1b^wwrq~ z)Yz}o`v?&HxrJ%>M?a4H@&;MG^=j-Jt6j>H*iTy_-M`G*M!aV+OxYuoKJNg!Tb`sH z62Jbu9+8~IRfpc^1PgE5xDow#xk?Q4VUHfmyMz3?^4cEg@aB5%9_fM3rgr0U8tvPe zzDpZFepQ1}l4QM9krp#F@MWU@K5rwAdc=SEeTeS005ZFSWf)1~Up39o%*C~VvW5Gc z^vCTLoaC*PclY)PMptaszmKBQ-BMT9=)F_>yn=B9Ms8dfF|=lb6E{X1~) zQjD%;M$C{>ihj!6i7Wpd)Eg{llD%1E>G|Me4iKp3CnoYM%Tx6w^z8DqXVHqnu&Ec^Lvf_?oLV6?;ZY zrXizxrLeMh^8I%>j0o)+jEtDxr?$qAY|al4B7NRCGOb%`!E+%*oOhMfMWL8AXX$-_ z8laKCu1>!0zYYOx=}M_ty#Y#LApezU8X#Z5*s}p^lL<4zpDQ~pn2BZ~+9jQKy zpoC|0chKF37Gt*HJGu zTwYDj8g~*{JQumY|FvvAbh8yn&vR0s4Q7oZDZ-`Z-eXhIprx>XK!+wRkjyA-EB6+h z#C^Wy8|PH$#@Vzsq34_~pIX2*V*Zb&GYyC8fB$%!Bt>N_+qBD)Y57JGlO&-mA;~fd zDZA|Jse~jZyRr<)9)s-L*w->4`!cq%Z!;J(=FB<&^Ly}r=FwbpU1sKdKKK28zg}#@ z-N0)@#Dir6WVdsQ%PYUA94`=~&HjJ~+W&d+v+-B2$77L&J?4B+EE_%T`}Q8}?dL9d zV?$p6+8O#C>Cft|B)nWTvPDWg)~ScIm8O~oGpZ9lUrHcU_uL;4H+fQPfZxxvVi^r2 z+2;{PB9TIn_v5qRpqE{vu7^4w6zW{tG+<^1b^PbZLXAmm!}s0p-tMMQBs?Am6~6i} z6ysTVx&SG7i^G~TBtj&h?4JJt=4PyW_Da%+vuZ*QLYzDzFOvtKHf<9xL0wZYvsij1 zGz;$*KtkPYA6V~giDtanMLBm@(gzfse8B3p5j(%Dg$%%=`B3$0aAFeNaK6CKa|wYJ zmL#*Yc`>IwU5_o}v9IJSQ8!w&=Kj-IK6vhs_ap@6Jyq!l*u!gZO{9V9S` zxuZ^(Q#j^h_jQ^yl05K`DxhU|fe{HGyan|`Ixi=;y%Z3WT_O+i*uU|L?9swgCRgVZz9;R+2@y>DX}eCGGaTs6hvxipbny2f!Zle_lGlRMgdUDk&9tulPE@a6FYUp zlw>d5EcVF@oPugLQrmX3 z#Kg-H*uWMNlb}sdofXCmS;m4AehYrKz{Z(|yiQ^uY{T3k9w1sEJO&D!!%pL^yC!(L zJ8V~o9HsAp5^4GcsZ zcIDVMsUWYSr_-_SE4A#IhnG(oyeBjS|AR~eWAs4kIa5_7a3O}Fprvpr~rj1=zij6Qai7hEb;SQ1f0l++}blhPUa@Kyn6}0 zOKte#h>%ZX_^A5HpKLaipo45_9vfcS2Co7y6kiHI8;bb$(K80Y+}`?1+uKqgT99=4wV3SL5b1mZdp-P;Jn+HA}9TQs7q26Z#({Qh%Z3Ht_ zoc@C@VfUFaWJIA0C8nE}1D!{x#3jh788?GmB(Mxy+})quBiOdA0%LGSxS)TuAe}wW z!s;Obi$J?R5KG|>sR{|yyc{Z1zDT4}4AJYL4Gf$0cIAGnl;qlWws?a~U@bEaN_kkU~$GHJqf3pNl( z;1bxZ5oiZoceB`fN3i^GEQ{Fd)C=j}O*#@!L8%}w#k)J`6}p8*IzS@Np0d(C99@vm zNRzB8v`G%cb^;WT@EK$v0T1NHPCw2j^fH7@WfvLITTePM9qKM%*nUa)7NQS6*Mk}A z#9a4N_JuJ$nAPl7@&#lw%*`+i2U_L{LDY~FAjr^ZZaY#`d4#-*K^)ml-mAMYC>X)* z--9l+NW8g7N2LJEOk@U-1S&%bO=t@0ID#03P}_vt_t@lSIP%Oo(AYf_!jQiI22y`( zX3v_@j>XSm{t?eM5JsTvbN)U@*Z4Os=w5^Pc8OIVXhod3>#@j8_rISxZ%O5cm-uMN zt4PYz5iv8J9-=t)L@u+)BObZ4l2DaDPSllSQsBcV;-se>B&%cv+N=^*ka1XOV-q9L z>Pu~&b}LnbK`W^oX)is;>&767AF4zocE)jmcoJ}>&4ajaA3$^jhI)fR zo*u&N(|{iwL%Lp!X{90}2nJq(?kk-DQ>y8OWnJY|juN?W^0Ro0R)0DduNXUEBq>-l_lKT0x0) zVBja-3s?RS8R9FvkM!;^LhWO6L3-9D-c!a#StAsnmi?T-C^Iu1|+Sj+HNI4g% zmDA2$micKBnYxUB_pkDyuyKKdiQ@Wi*VC7p$q(V&u<2Xdm#CQ4gze^Z+;D!)XTJeb zU*UCTyx{fsxI4n>{auR!e@m+qQeYo2Eut4~=*hDC-OAyQyV<)9jNcd>IzI5x$Sr45Ma(8@g3Q{Uk@*ESAqZXw9+#NeL0X?wk&*3FT%9kwO$WddP9emo< zUA@6|ihI?VH99O2}p5H$p%TIJ#EvejKtAF)@p-VVqKQd9T!LTC|K7wRrTz zWUy@H4fouYkLXZLZ?*cbdd8oX zJHGyv!!?TgqRld{)(uYlIVzmslr z+t5wN_w(e-&&oE$z-L8&b?9;))At0AA%S+U>1XbC{WYf)xp-_s>R+w_Gec7Z_-ocM zuX8#_ZhoS?4QE4|%pUtSCdsNBzqf8~R7$zzRsUFUk@15(!9S{Ci2UcSfIV2nqq#cS zVh6SZ+KCCkCDK`*PF_jLS$vd= za&kMuQ`i1@WgF3nQHkCQ(}CsWQh9UV@d=y2IH$JHrIj-G<06*iURkZal2`oo%3c7{DM}NG?oUO0fp~3R?8=0L8`c-D+=s^}y#*Ro>#s!%WZRiR5bn zaAav;mCp|s8Bxv_1Ib!57`(OXr}1J_W>{UZYPdVu z-;)%0zi_|B<%reJR>6L+cW;+mt;kK;HoTq9PZ+Wqm3^#iQ5s#Pipt2_mlF-$gY~<9 z>{g(p=eY6f+HnU;CqA&jW%1a@t zS-`9zN-SHA22Zfoi*$YWBy2k1%zcO7aiS7vyq{JPWgP6aJU>7rJEbQg994czOa@uiRPpP((|AX^?XoYd?s66MC7PYuyds1qCq5 zM!Ug@$GqQdPuMt(&cEG%Q}~7b(~pLK=%e#EIm=x)lkg<>1+^w659?h{l<4ceQNSuH zdEvn%rWIA;ZeEPA%P_a0pahX=oiF57)YD47I6=4;EHx zB5jx+lK==g#HfnZvh$E+`^eN|Q6C6dlZe67pI5rR8d+#Ec-iZxfk8*#;^pn9dE*pa z6URRHp9hmucKhv~Iq;W)*O10iM3+f=T>nq^+HYk74y&S8Uw8Su&!V4vA>wvDKGn+a ziY$-ihTd2jY8)whz2q{hrcK;#;iUIxqa8YTEFC(H_*Sz>7yER+I$(6fcn4=3yv=WJ z{A)VL5ZxuyRCj>cNM0?YtMJ23r`d3at?328=#5;g$S!fy@45RwVua`BO;Hxo__P?b z4e3Y7=<$M+I)lsV{^o|d0eJyG%+IHp-U%pxf;+vEyrlkkfgOvOv1c#f3%k6VnCoyf zn+s_lTj6^UG8}mme}K%EnTPe@D0O-)h0Fyr9(Q8q#o+$+%;M4iV(;8sI`>-XkivyS z3-6L${bmKVUBfSiboB}REL0^<5Pex0R$N(`H~ys-X?MLV^=IrWbBNRLdc=0PW_$I^ zr4}P@!Tahp->tQfWcN0)fe)=uzf&kE=ZRZ%y2*-Ed^>UOo89oFN?>xT=g?l~J=ITg zEYXPPh^vy4nOIR)CFWv}Wkr;i9U?w&y6Ca5+WDyP8x<*dqL#kZ>b513zWPI7TboJf z+V_Vz?yu8HRLBZE$WsE;?fOU&SI#UDZ^OyP?bp0+Rx91zYVhYN4vh}j_@M%>R~k7f zzWwpWg^90CeUDJ;MNBi{O;Yt+4#kzsL$KpcJI14t0k=(f?|Eiu>fqa>cOdrr3#Ye3 zDi`9iKQg0E-Z)wPo!{;Snx8{puiD<@YDMKkg=)GF|M^|K7&oNYQtS-JJ@@0 zWtcMNI`!hgi#mMzm}|GPFGmKl}S% zVnoIkaz~h#ry0diVZhRITq$OA4L>!CfR${W3V#A0{!(5vr9n2~lI43duF5r z_moYPxpx9z{omOMjs4>>MB`5I%kbWOgB^L#zPLnK73=HI#ONNo7C9a0s z=)FmIA1JO~_phhmo1?F2W0tq1DDg71o*39~aJgz~aCi=Z?O^?wLCG#7ue*u<^pR1Y z!o-{cy?4S3gfWQ9cPh9X@cX>F{v4U7KR7}zbubZpvePx&CUoPD1Xz$v8N`}&J-O^~ zNpVl-@I`+Ob`WxAh1^hrjO{xfodPNhGsabm?(f%oOI?lH$gk(k9PSVUA}~uXTLg(* zT^FTWRwgIllRz^Epm~xZM6arEEef+eHwZuIoZm+hd-~v(>&4`?(bWrnuU~T{L}40? zScJV9eM2fP@yVl4qA(7?rEc%^kUL$5gIjb2|GX8J2!ulKV}Ajs#m{R zoH5H}a@uIX$FT|fw(XyDFwEuSv4h#a3W}MRP8m3!CAN`uzqi4;(}dZ_Ab}q?Fi!!Z z9_^`7hkQZT6a!wO0#!Y*Qw=foI`jFJbMoXiaFM78@Z^0Cn~$jk>!)czeA5`RQtkY9i*@Yhn|l@;WnN=+0RskzAj=ZpSlYGV>B`Vspn;5*xi^m zBeQGZN#Gw3vesl~ZRDJT<{f{Z|9D~O{=9}9k8kC*M-^pKe$OAz!&j7JfAp#`%^c$| z8_PFjdE88F=v4Rp{lekzxZTs`4*%5j?j1QAEb z4dmFw1h|Ure$)uj{^D;0zLhiZFf_czn%~f3#DC!H4m!jIe?k~nbVj)_PzpsugSJp5 zh9Cv2CVzO>2?@2KVxo~q*m9K{sB!iEx;0V^gqLj!xR~YBm_aXao|h2>;*0OHW@6J< z65u{L-KpXkR2~(k?Be!Frz6v#aCX0()r?bdzWG;n6T{?{$#=H36AC^&imG7ur+7n? zY$v+il$Ifw#}hCD8T7z{a3jNJk_=o>wvCG@26=1a7$~vSF$Sl?{T&GBhpvS`k%Pg& zt{#92Nw-Z6zpil=C&fWHR%WgVI+leVk4Q*aSP%9AR&SO z)f#nuK7liQ*Mg{n_rCvx^(hQo=e&I!|N)|V@Dw8f*+fZJi?+@x0P%qKK7GoxW&PIhg@TEz8MUpaP@&e>uSby zbaatT_kxi}rCKnH=IGeiW^AW)7ts9)Lc4N6u3}FJpO7djHx(Uf?T93$^q%Izfq-kO zL*dj#4{qK>!eI;rzmzgVw!v?tGF-cPiY>9^iO{NL1otLp-R924_YG~WJd#KthCkc_ zteRf|71!o8F0Q$%lw6)(vPq6y!fX@osi(2ku}Koh6=959ZYQyFz;Vapx2N4*r49ZU zUzhY6nx^A7)>-Rwqb0`rUtxO2{-3oTmU%{51%I&rk>>n~?ZR@VUC%bJ(1Mr$qrExR zvg4`kVRFgburR$%0_AUy(gFXiO0fw|s<}I3%UMZUY6b|E2-}H~(su-b`uYHC3RY9p z$Yq#81n-MJ7fK}9jKoOl+xLJR1rnSG2?zN?<1bUh_jmqmuy@B!b{Yyd9GyWu?4ij2 zIsU3Xuv{d+|F6VFX-Eq=)LBH3a+IGge6`yw%=GG0)*0?m14e z7uhkeIqGMZ*XP7a;iFd_q zzQo<`6V&!;qe_0;`N0ladUpO^$}}(RA8N1z&Y` z14~73W4ety&&IVF7zAAT+;ZDViCwq~Ezjh$l~-3krblc9Rot+BZnAXV?^7C&N}6eh zh?ZiumaEVP1=Sj# zyb><_dT*M?o|@0e`x1qw$wxd+th9g%{cKG${G?VglOa92gok56g*2pBOOu8cOk+(7 z;Op<0eziV<(ymnA);mU2)X|HkM>a`u#>Be#YZ%9P-Mxj^$!0GJb=0b(3ySsIqP7yU zdcst4+kIEa^MdR9@?npVByf+u`6Tjnf0d@(7|M1^Wr~`sfqpF1R`ldDWOEjnfIqF` zUi4k_og)B6A>v;3nU7kYG^%rr+i$B$Vf-e3cf(`} zXcw8me!sF5$-r34PTecdpVjzq7{$lD_0VPU)-%!UzVjwuaJ;!cfHdehMm*|Y0WdpX zjx*llwRTIXre^9~LD3_*(c&72&*6`Cd5JffwBsH4DWnlz&B+?!mvgx_`4&5}m}S>} z@tSLm8#L^@UT5boyY%Pm?ykq5jkL0ki;20#EBkg$5|!OQv1xDdqg)d3K&Ib~;i_Mk zTzaO123YU89d?v<4oDUWzDaHTd!Jem^wdX#llU?#XGG@bsT;djaWLnv{i$t0K(w-{ z4j2h)g_>AC-Ln9y`v4m!R>QXlXKNRGD*fW>`n5kW(uI9p0wQp*#>DJNr=)sMvB`(4 zseTU-j{p0sD+jWMd=}2E?5VIi(`GvqF5^7r)%T~yPhf#QFY3}Ny^^u;ray=X3MMLa z%z#}}zT<9D4?d&|&XZjpQraO~pk~0i*?La%u;`oq8}uC_rb~V9D7mv{>gYEKTHIt_ zM5Cve-gRXRedtwna%EwQjq00 zOGuT?_37Wuk{P^rb6!l2XHivDK!V4z;YGRQc+HxuaGd_=+ATTv@13AYXVZx}6B4)Y zP&2x2gG|q?U2=h#$~NCMVJ+RD0_Nk~^e!E>j6B`4AAM9|8C(C(`=2e2a*_vsmS-Nd z+W#fLY$R&aE#NPar9`&r21m46s z4E?n_Boe+6D44}%=I+wHXyu=Ecag!})@Zz|hvx?b;O6an_}$QbfnaXBP!s+Dh5}st zUpTsLlc$QxpB1U#uSQd8t@14SD!l|8F6t}_4GXInm5enJXI|n{Hr3O_=IoR{v5Cu zne!cHcjHvx8~MvQHY0gXb(UHpD=$UBX$yMEk0Tm;W5}@M@!%@$iax#ot~+%uhwe zeOblMJ}Lg}VVOj5%&5$(Q$pq;)~dRYXKqa-MBi!Y{HfVXp~!9@IHdf&DRvd#NxaL7 zZ(r}!9abqnj#Eh=3y>#>vCPwUhJ0r{PF33I4_1MM-iku8t7uA_1&4eTv ze8PtlXEMzRQmgn$eXqZl8RpR+;1}AJRP2TQG~%78cD*`V&O4`6lF@Gr*3^SOXaz9@ z*AHc!&XF(+vzXTPdz?fw3q5<67R`B2Hl61H5}cB}t1$Nm*pw)AZVH26>l%p9A1XOg zM<~qb*R#xXz}t|%Stc+3?b7&wR=wxPaUT9%bi3~3BzofC+r`(x!Zi^L&FrNwCy!Lm z*^YiT8;q&ge_{MRqRj#?!MUwCS{qkif6L&$dfWs+-bOPOi%O|Q_9iF_{NiljIT^vc z$=7=xtKMRA9$Bz+cnY3L!}=}?ER*8?d4qPrrq(7!^+*_jZ&*c#yYM^Y2`{FceW1>KI4)qGy;TKunPQn>kl)0j&6Ju9lAk-#)e2gu|5l zBCV&;2rBzM_J}eIH+6Q2>uH>;b-wF}js$UI`E@O7I)i(C^S z0y+40(JVu|jf1K8A@9CRlQo5KFy=P&b@hnE5~hkHDZP`Ki^ofBd zqb0Ik7^Ta$;D?=`&k;yA#5uJ07;9M)D^br)bxQvu_e(Ze!CT4Abxi0^cFx^nN=o&V z`P)>dkTQn{70bhex6OWN#dpiz(S+i=8mP2(MC-*dFi4 zhKhoF%*WPyJ86N0pjz@q^N%>Umj@fL8HT@}F66cRq2&z7WQ+dpj%zfmzRXrsuFI^r z;W6e__Hca>BBw}Dwo~(uK{L3EyID19D~E=Qlc0vdeZRRvp>-Gb%yLShUPU?FwUT(t zb>)9@CYPDE(dy>uoaj2*tOjF=c|^RCC;W0b9SlI1(_66(`SNwc&19w65*r`I$|w z^h$;Z{>pSw&@PgrY5zz{lYt&zgjsgh8{4xA7lM0%PNE;E27M%CN!z{$nuUtY=KXZ< z%J>-=w>Fz>)0eC$mPTrKaYtTy8GLjjD&TvpT zAr5Bev^ujRO%=*}v#(BYw!_IRz_@{l8o^+s{AQzI$_$Z4`irDay7+}Zct9#1N>f*s zFmrfW424T(31BkO5(RgW^TAcvxm*AER0t`%_+G%Uy8Q76Xe1hs3^KDT0KZP4k zb$J&SU__1@s(S;~3j^UY}z$OI^wQJ7gGR1^iB z3n&D7p~oJr@T}`8Kf>D;6t&?K$W4`J03V4o%+e9wt^dxrG_cV&NC^jyoT`RnBT+?x zuo^^}H3G4jEx6YRXPAt9?9z0;v}1FD=RROi`YGkN;&5S60D3Z{ft3xv)!`Rvi-r(I z(Kbfb!>XvKfrJZp`fb%zC0Zm=;{ld zVoGR#9Ox#r6F=DRBDr|cz&FlDU^L2EGXgLMxf$XB65c@KMKVH|w+~=p{a*ddUkr(w z14x4kQFa?qd5IdknAcE|FnQ))9Xqx`35qF&iN^3W@pKfdfV%?89+@Pk#u-?~7Z&;|c)#uXA{D0GR`^iLvcKOZrdvg5R+G7O_Tl`kU03CL?z@ z=NkbOsLhrg4HM>2+J?2zeCICS4-3x%(krkp3pG;yA3XJ<6zJqd5H}MWR@AI3(1n_2 zq|4Yn_@nq)3AglhC__%oU4vQ+3v9yuNH&Y`05*ny)j+>M^boOX6*nR}d~>>M3qeY+ z5cyeIGicLT3hF`R99|lSen|N*2s}3kWe4a%u#X=VrPRv8tOYOhx*ACCd#*K#-BvUW zQ2sy#Iv;EynGRPTN-!1ssi6=l0x>ZWC#Q9Sbv(nPwvvxM;iy}hWdjuHn6xb9SDv94 z5S!TMiFIZ&qpM*_<|YH_GSzGJhu?m*Ugdd$hHb@v!SY#L0Gj7@Wnms#b-{KMCKSJj z`HX310cmjLBC&>To%d|vJ`+PvQ|N)FAd^g3299UH4wL-bhAOfV1$wGyRIiN#8f_>^ zc>-V%y@8x3`2i-VwNZIH9OK+eby*vNelY4Q|437H7@a_oaVQwOaeQE&c-lz{KD?36 zzTnu5gW`uVlln2s*n1jn_~tW9qBGbA z#@Y&3oyRHJ4PreeR&`wRzcmAqRW#EQ`|%n7?4VIOapU;UCC1Kv5cmB8FTKJg_fSBhn8$Rj z-Y|pkQ6W+dXNdvpsdoBn2Swy;pbqU>Y`5Ra-IiDNx1mnDPY1+>Sl0)MFy$Y5L&wFy z^1Ll@Bb0oLEBNLOyxONen}@%K`?5y;1esho$&3albbRCVe9+9YkfECOChNq8@$w%w zW~I|f825bqLw;XjQGVrsFykk3oT_{a`_J>cU7MauG!vv?GFF}H*sdmTg&5c{@1S2R zb0&^OLMgzzC0+ea8pvl-%pbTe>}h?sim3HOaGxfRl*_R&g^1O$;ei+0X^e8`h8;Rf zalI6y^k+K!u;5QEiGN0KI`$)GsOXz%m~Zw3>X`|9v`5hGAZFC^&uVQXbM_Z;yXBY{ zuniUOMczVM!0KK!Voo6NZ4Nk&b;w;;RvX^}`uNt0zx^rWfkM7(&cMqu3Bn2t+l)s6 z5%#VBg*b~xEuJwRc};H!S#!sK-XWibF93RQB|ZL4TpKYJurlpoWBAj&_x{u=k-TjI zUXZ(AvGO_kQca_5#hS;cFQL}H_T}<(oj+qaVhpr>x2NBVUi!XUOaDr~>Sv8?qQRzv zDShFnWn=R3@?rDp_2=Qkk~&cSrnRR_gKOA5m;-XL?x)#o!Us^4L7ult=&EC6{2uWfnOTbZ5)`qV>cmM`<`&qSQg5lXYq&wG-i238X|8!^F$fqXJL>O;>v=Y z+uvi;xEJr{h{sfwpgUJMI5(2v7|(%IHX~>Na{Z@!3uchB;1`3u#EYh3N1~)Z1Hn|! z#)8W#Dc=tKSexH2_8(mrn*%P%y_*3&S^9E=K1o{CFedIu*zw+rcV+N?b_Z^WPUr|v0Xtv%^{$iq&{d5+7 zC_Y+3G2rJ;itkFp_juQzdQAr-@)hnu$oDxAmVEgR{ngNBkyC>|OMvq>tTltCbzKxC zF;G?~1TlzM#qySPJL!z_2iAJmN*^z#*foKW$4~^TM1Fhji2+SP_L!3c&0vn}jYHVw zvchlf%Lmxib=lmMnm|WcD~Zx)N#t5}&rv&at=iGH6E#}|Xs~><&ZaNoIyLL7N27+s za2ls)bIRmjIy6_d|f_=%WDz z4jX?KcuOD3Zex^vUgy-`l!4Sa9*B%QqV-6KYpvp`__Fgo#MwMen1*TNR3T&||I`Oh zbA=y$6}u~PaRSrY{lPX%LA){eMp*pQtb(@D$PiR_PH~#8z<)v;hM5t$G(2X(} zm`+03#pw_*(MN*O4O51&gA^vlZQ+>jniJ{0ao?In1MDMmO(?+!sf@%u+hbTipRlB0 zyLD;c=-B;c`RXWU?nvQT2DXuy%@=?v(vnBuL}mHUm^^QhhV=&aSP&C$G0Q}4!#tbj zH(*2m6msFO7xR2o3g~yb0^U>bgVd`>Y6WFXErg0JC)L-14;ZvH#n%HFC=(l&L-*%rhhH z?kwAlt`-K)gz=w&tY&bY6Yk|ov=Gi7Lx<59+=78c-KxqGO0<_hGFBK*nxL)utXc=h zzlD*jm2GQ|@T$keS252W4eB{K`SGaL`s@)rDFa%sQ!`X%uUyCeR>p}b$HHQNsi&8HOeOM&FZVA=IdEL|sk_hg$!=kv zTQ3i}AMRmR=o5_i#l%lIN3fpE0aQ(UHMSUL>RBt$A7zvSQI=CVp{b)$c*Z<9<7PI) zX%@1aF zu-2;QKO=yUXR<`Ms3D~6xmAzkO+En&k|;oxMY^?bTJ3m@_R1c!Dp$X)bvEfX-21Ep zHVFD+*Isw34*c>eWdSQu1+ho6;V*FY<%%bw`6E!LG#}G2xEyZybQQWUuFlG$=}X40 z-g+n1C~YbUxtMakVZ0l)o>kY2IzuD0O7qS8kfl~`e}BLCvE{=>9;1KCs~D77l-HuQ z%(Ky9hs_=v)M%v*-lQIrX!Z>XIj`sh2i@*iSg^!BUi;u^buua(>t3R-ZJj3az6PS3 z46ILw>9geRe($^EF?#~?BzONRIDa}!(13pn%^Y13_Nb16Fg~!VV%EHO4unh;gOwW3 zX0#JGtMGOxU_;~6|72}LE%Bxg1D{F6B0?OeVu=(h=Mbr-(p8U9W}mJV9B^A~mQrs1 zXqCvty;fk!;gsbPp&ol~%d`l=yW7NnBW1%EkUz5u z@9xI%^@N%G#RuFUa~2DGB=57!b%JXZhrGv2Ev>exipkD3 z{)(F-c0J#N%-pyC8bC(o0WAl4Dt>R1*-4u}*#WFd=tiOQ#&i4hKd0}WEBjjur$lnF z;A#ria2Nj4(Cq4cB4~wN;=)%=i8IMp4*0<^iiL!XWQw(9*X&%0$#(8bgQa#SZ}*^fm8`uxvPF@fZlvN0WiTg72z-Kk>XXzW^@xWM%npOdRkyYu2@P zZYmn|sfsB(ZBx=K{ByMZ{)8*WA<6Hk*@mw;wmU;bjY@dg3thH!h9*^csSPpD+)Pmc zTgL-vg_smbF6{^r}KD-_P!}+~hyO%<{1-^t?K3e7QK;V)+NVP4?MS zeK9Zrc*U>D=*0YkIQ@o=1)%4Q?&x~~zh|v;D{>j@RQYKwtm%2Bz-~pRx;A70e>}B` z2EKpcKgW!0bB}5>`l^7}2i43Ba_)kc0|~;fzBs8dx`7Sr@&q7fEi9eQ(dML*nTq?k z4(cGGfSgZVh55^vNI+71uw}|>?mLw^%CzP2HY3C=et7cQx_u}7WFJ<5 zA3htGCPl*_dErVvSk+3jeAO`xm5s3OWAG$1{;ms z7x*x9@M|O~t}5|u4~PlfNn z5jgX^pitO`T{rJ4?n+NN^O(TBj+PM} z+F;h+>!`_|$R!49dmy$4GCgP$Md$oE35P-g=Yr6v&Z#Wr$mu+}X?*8l_q<*hIKU`2 zm4n{-2mqanSXxXwK_#GLT~1?`aCw-It(cW@lu90di&;4D0Bo7u9`wHuf?vSS=fTxa z>oP5Gmaf0Li+>R9akkFS;p&yr{!H$O&915Cryq$)Uoi4GYzI7rmBRF1-T0wAYzx9# zyYV}Gh^YH$owSD!6t;?(l#@(k(4~MVfGiPCq=iAwg-DLo#c<;cB*ey5Is4ba0@J)g z21po2WN+tH0jd4Uz%3;@5&85@!L}f(68c5Q`~uoBNm=4RAX`9RmDR*)K8>HYtb;Q| z9)r_Vc{}*kvQ#O+1>c5TG)ACM;8Tb68l2yXHD+^~R8PE5%wiI^h|`dKj|Tnm>8K#? zRi_1@mBrimksfkJ9QI4QHt6%pbVRb^O3P?@!AK77wlt1PwQNRf=|l=MX+Hkl!vrIvM*5A)jzL8 z_;y@eoD&ef?e+5sBs43e^F^)=Q>QT=kgK^>k5mzn=|FIl2te7Fk@Fn9?mXB<@mgeH z3xQL91%n6Dfy3lcEHjoyu8)7#o)m$(0A|H{m@nS$xCBdo#%KsiO^-xc6tUGpI3TKt z9CT`y(2UvWLuN*<*ATIy6U^gx1$i_15$w$wAP2s{3>np|e6PH5m`ZGhyqPz$@t=v3 zSE)(O4)4t+&z1rjEWTGKR~Etjm@c9kB2=N`{_1u^XP{T$afrnx*n`|$2vq!c`=i;S z`(u~rz-DZ0Hc;^_P-o^o4pkqTBGhp5c`K|q8XQ%c6Y9Xr)gApuP9$#V^bwzk5OxRyFN#wR=n};zg4jg4XO$K znTV$#QAR33Q{Sv9#cxSRjygli|IV{RuITu0j@tGyIuw=wO|HV$P3NITSOfpiN_OI= z-N=ux4&;|MCcm`BaJ4zw$kCoY{VC{nQb5h4Z}bc@@Q;n=vHWj#wyA0bAs={*vOvCh zB6#=|KX9X*#{Ik&)6FDsOpV%)j+Bj9asO-Z2D4abX#x?^+d*qO+z_bu}7 ze~BaU3hv9#(lhN$YM*W27H5Ds1%=Y!akswk9IzXR=or)-MeR@x0{TBkz4P5HmG}2d z%#r!oldJk@nxK*)Jrw?vCz>3;B)$*PwGY{b<*$71BNfJ(IGE9)t`9oO#Ucc;=XgJe zt5^$R7$?~OHd(=VgQ~zYH}}rh#8MCE-sLzuAOAePtZ%LuvmOylCJG9!mMY@ob;YBXZ5iM3M?fQ z;h(M!UHN5h5V}RYD-6*3TG2UkYK5(XDVb6wlp3W}kUSI@o{riO{Z|ss;fjL71dYu) z|K3%({M4D*g9(`gqTuSEyZQH^c8tMPwVcBEFVIqfduw;A?h7j37*>fNz5hqfjoE@J z7urcC&ZVtAyyDIt%uHF9Gap>eyTa)I{dyovLXlB)%xLv1dPBr5=!R2OovZyp(e>`e z7{ajEU8qbv@usN@Q$Xgl{Tv;yEZ`@&M;m`_yz^_?2h+~d5-b4O`@O$?)%L=^B6|Ef&PKO z)Hc*$f5c7N73CQbaLwa{G6r+|&5!Xe)y1oUZCsDm2K~=|?P|kLmhNJ{C3^O9NsKi7 z{<>Lp5jl%Wu)X2<$G0KPfDj(W;V%s^ANYnEug|@T7LfW4>(A>|7H_q*p@|7rwZ@nJ zi#O~14}PA1LwKlx2$iyFKtT8OKxP`-$TG5(m?7ugX+-~*fEm^Y0y+iJrVqvJhF;1X zCW=-U?Du@^=XNf&aOj{?8bSl{CFFGm6Qc0DmkKJ(E4ve??zy#Ptxddf`8|(qml%UO zK<+w?WQE^}zyPWPKJHphS_|NzmJ4$4A(SH6j@wCH-uS|LGm(;Pvs<6Nzl>|I-1ol* zS~FTRWE_1E(5^%Hjlsgvgyl+)z~H-kI4ix>-T(a=diy*=U|VzKX*%OHemgg zlUj2NMWByd+P%T^bk8xu4ya)XY~yK+!I z2xl^4Oa>r@TWy#QL8`lh0^HiQrSJY}P#;Kw|CR4kyb0eyz3JCwEpL((Qsz)j6??h& zxYEK@qjY!VYVy0LaLBT~+Oaws`{y9<%cd_532_fsLBqT zQN|LoPO^+8`%W6WY%w#InQ`V^pYy(bZ{L4l=7*WvIoI`kJ|6dbQ8iDY_(ZhDVGByh zDwVx&*lQ-$ISva4A*Y%-XznAFt(hb)%k#{PS0mC!k35J!x)DO1&&%(~goF zWhY|#2}N4&_t1&<<5OO#&*ukhCDuwt24GhWk$&NS7S_1hKX(~Rbg<^1Tzgev5XQb} zxRe^JF%+x*Zy-T9fTznbTSv$L@VI(XI2>*N1V|Zwo|Xj;(8FlzXccVI4D<`I zW&dYDA-)Xw$+h;&l>eG-zv{2vK6k;AU!tujggDSuzS#A1ZV!a!tUpvO|8{V@MSY`W zS@FErGXK}7wp2H@<gf1O@v!Aln<<(Cv|IAGo2 z_ec0XGCW*Oxu>(YUK~iO=$o)zmG1^dBf$NT>TNhojZ!G1tyolMf16@(~p2rdd4) z<42NI!^LvZL7zX4NYFn=ON6eBg;t|*$p>kvoQiXg zx^`H-mxhP^Dm}!kr>JCE7l`RRu!PP04SEb0nZI z@*nbs?{OFO4J;==I@)kG`N897&$CL^Y4P_zfMQSjpSDFW$?sPXi~((Rm_yHGlD&|@ zwIXyA19W5TbSWuFzlFVgMd-kTG^Eg03&G)dFGIMqS^w0or~BPI;zk?^9p@6+XCI^5 zSqzV`mJ@}nChX>56sEbfdd`uENp2ljwYLRENH)}jStJNThtV1=UbB}&4<+-gv8Ti6 znO367ia~EYYPbBzjSw_g1wa3%kVZ(G@B3*A$gYn-z!X#kt`-*Ts@6l?P>!8^iR&0U zlZkh7s;O6B%oRNLaQ-VLYv0#EPD?#K@fD5&$!n*!bw}-o1ns4~%tiKL@c?4iX85(I z`m2ETd+XoPEyM%c%7`UGaA*B}NWM~R8|2n&tXn1n`0b^aO`oUT2O?JYUHS`6Y`=lp zg;D=?5C4mZnR@4ksgmJO+^mEIC`6(1@sl2}zB?Ww_Jx}aDidI0(SZ$l|B-(WD_-zD z`+Deze7*UGkJggFg3UfoG0Ku-A{!E*qAeM+P?iKP@~P`vP> zMS;387r09F7gOv1bUZJ^QxoQ`INqES(Vv8@PlI|3Z3@`p;3*j^m^XLjMSm#grW|zoE zQzQszmupw@9Qn$h<8g;8J58?jeKE(?(YF!!4DNQaJT3QsWhxJPpA%CI{+=Bf>jL(1 z>^J7OGRoksvK>-#Q+6}BsqG=pV4byolPB66_^4B&;@Xd{ZGX)+`OE{-3^VeBt%{SV z;;5Nc$3ODp0S|=(K5M5@)iwK1q0{5nx{Ks}+7RPYl$Iir^Skl$nN7PjQJIgd=Fa67 z^L+#}v`MjW94_R}=z8g~4`=&*zZ}{RJw;P6D`rQjdEF+e=NnOmh@~~55huXr9<9hY z)NI0kYSbR0(U917mrAv!Ithj4<6QpH+^YVVr$-<``uyH;yd~o0`orAjH5_05Xd@!V z$hQGFL$P|2B*J#nLzTwJ{Lx8o4v1m9YzT#>K~l(_u(L?koIcXn`xJRaIC+DIjx$Io zZ|rCWG-x$gxPWQ-A{lB2Q5CwW6DE78pEzVT>d?+fm?fa%N(I9e@+wppZVkUWlMr#~ z3G}m_)eb3)&%@vX90#3A`uHO`9(_A9v5A0@JZX}b)wHD$rQ;UCc-8&XmQ|{@G}RO& zj-#Ptv#f2@KnC?NyK!vIU-B>fdTj=W;?VS0$)gc#1jIZIGLBKd&M;S&Jpb4x7RtZy zO4Q8z`eao~{udxyGBrdU%{{>=y$&Kkk3IwMbxFJ##?=zf9zZQXLZNxQjU}2cxE_3Y zsuGbJV@~SU%Lrw7zbeHoCxTSzE)Q|`o7hCP0Ho%G7C0QY?;v(?b8u>wE9&ZeVQvWv z_0tgly7e{X7^tSJAi+_^E*vd}3)NF#a5&e`trSHyf*%;<))c4$S2*qA^BgApsm=p~KV-fyeO^1P7Gs``P zT}CkrB%mrQ!a=|)j7PAF?{gTQZ-09DR*3bGB2adiHco&r}pOEu8b_2Bn2jun2;yL-fGUApv>~tw}f6mj8(v|2DH`^>iJSOuzkV}whD%wmi1SK z>>~YploJW-TaRe{h3vzE|EC0BN0*|gFNT4iF_i*DK7z(p9tHPgNefv)1lCY^(mFV9 zUWO35?mMSktb<}0cl6s(8qm{t0$cE6<-Bs+HHMOu!NCHabU=GDi*pKFzP*r{bV7E5?H~W#ZTPvEhjTJ2Dd^2HC*~Y^jOe~1 z+(`X(d8*VBYf(Gb|J8GG?f1yxFP$CpuNvr6%hX1yBl_UcaHP2SUv9-|xyzsZVq|o; zd+>z&Nr5NTgr8{9*%W6iB!9!?H>u>|@5Lwo-h7ag|NS{UWF_6WpGu1Ki&7pZ)75CV znxT3DgX~155yOOme66jyw5?V1b25dl%(+M_PcOO8mu99X$jyn!m7h~nIMxi-!JHJd zP(Z^tZT!=%4BognowN#aCvU(o;jA<%T^2H`APvX&kYX3=w+)xE4m>8{QBKYne#T)Z6=d^?x!{52hN&kR&>MBeFcFMT!s}0e{PUf zMpi~28rV_oIxe~W6#o?r+;%YRRn?yLIgs?42x4USI)%r+ae7$!BMSkYd~zMhn+4V2 zf-{~+1UB`%l~niZ3#Pbn)MlFFB5b$@dXy#17ty2%kV{4x-)+d|!K zu_>t@Cn8mNGhdW7-(yI)@b~)gW7hE^(2X<1=gUZE{;e}U< z86CK^wx#iD<7wKgwO8jv%(xI)NOgG=%CnI*L4tm1(U09Pt4R}QeI(-_7z|^GnEXLe zXCsI)*%2$TI4P{euN+Nv1t;=u*o?q57Pf62A+c#r#SsA;Eue^lPiWN}fAioO81biD zF6m?MMpO!ot>pQ&y>+szZHV!G`>cl`lo^Pgt%Y6_cvMOfC0I$|vnD*c)ve_yTXW&e ztWqz7ZslNRG`jK_rIFL)R8w`soF2J5`E?;`uuNF1cm|3==0#cJBi>u#im@! zNF{4-EZeKiz&;xH=x1QnVLMiABrZwjGMc%Cc&H4qE9$ zk*3Hqs13>sdKs?bg6P#4p+>rk+v|A;RYmUV)u=q0qOSPM9}#!Z z>S@sw6x;5o1*{dqF9+wLoP?etQ}9G*=s*|4jL)_!`Mk&?VF%{N4>Z}?s!2w{NtbQ4 zsCGLS1uO=1CdkE&ubxq8+vTQ;B+u3rNxO#(ReD&J*xj9rPUqV#i9QzHLwz6qynAZ@ zRR5u#)6q~AoG`)l*SNV<2?xL7b#pxLM^%DwKPd^2C0C_=$U5ukv1;mkF!Dr(LuHTa zOJ4Fy^VC*aA0F|TIHoqJNb_{pbswy2P_S@Nrh_*Ifl!{u(4*bC*qAoT9W8IZ^I!Av zRJcpId@aU;58&S8*_NZ?9Bvo=(DCdAXIi6s>%(rUmH2eA8T7=ap{5bQP{5sVWSBeNq&GrlvF5H2xD^hr1h*Ltc9C|XW`M&W z>Ae42zcaUQiy3u(y5yULmrJ})-O@>LMPp_lN64>85%P?^g__euj zjJ$OksvM-7zNXSMc^+9d!(Co=C5_Mqii(|ZKa@hFyXJmfaN>uFN_KH$#if7psem)s zgS;kK4bwyVZ62z-+`7rN+GXghrPl)jkK2}x1W4DM_zV4v5FRKg)2%@a`tQ4zj%0WQ z>z%&1ltHW&=`;eXhkJ@}>dV)fscCXuhiJm24@mNt;UtOw@Q#jHs#LZNlNiOd9`IxYN3SxKAjDgKVp zC=`D+W-&G>bgANv96J8g85ofp*9SZc@$dS>7x;M4|8gaAdHX;H++VPD^i#6OG!xHp z%}Hesn;4^DH|6|M@9x$MUdZlgSxIa)+ingD@^|*sX#Z#1LERtn3-uDJmsG^UI8wM9 zZoZ>t3cNEUn9rrxL^XodKKc*CDU11A!$S{6YN}RXocko^>ONR@7y7|lE#ac75)(r- zZ-RQ=uGB>W9UhiQJqnW54Fopfy^y8;>Z;Tws~09Ay#$cG&V<3X*Rm&gRvz75&v#2s z!=lw_b2a#g+NN@n9Othm29d~oV%BO9E z#>18MeHVfcI{Jiu1I+RE=0@tN33@Jg)XOCdqJm95X|e-<5dW4dZw3Gsk5EQ_nbeBN z*^XM5z2URA@+eSo3cqR+olQ+sX<4N+@^u#L?vLzbLcCTvHP%AW;_zTmS@m;YmD$T` z3;hwFxM(=l|2HbZg8dHQSDae~9~uE>?*Ou5x0(LE!2avqjOQ1a)f?gjSS)ki^2bT^ zlGVFs_>IK2mRYJ$v`X=@k#}-Of=AQ%jbUl9@^R#vv;d6wWI!}V^Bb)BW$&~nk_m(B+#Cm2M;(cqb4rUyY$H{&A`v% z?Npf?ujP-;_QkKS_&gdqNo#JTF!7lK`cN%6}JWqg$<;T@2P4c4%(H+$*3y+!z zvjqC8uCE>or+FH%GFKdzPn4cvN6mkK4Qzm|g(M+t;q?I})rU$3!hfTe8cTi`B$ryp^{$UQ49V!6t*UZ&lvJe00iFN)- z0yOVB+~7^h7v2+epz&(=@`p_=6>e285Fus;{|v@gj*iSs&WLX&#o+EBNz1~e`cwW$ z#*1sMqu?!E=x+ItD_`U$B%2e`EtnI??^Ga%Ex!hGq@PMQe{;nGnW^y;G1zF8vO=I z$ZTl~cxr9dg#D8%clYlN<){|ytPeYQP0OjxY$Kk7VGrOMpmki^UZMkV?*9vzoaJrT z9Aw?VO%%x53Bb}=(C_OjnAPKI*GgYO&7!{%bapy?R&r);ja0*)VVIZkaUzJ|fyMU! z;C{VaNG_!TX(eTO93+gu}BOY^QY+ppX0O2P&!@xR* zdK=_&Q2Y8658HwZ1Bvsfu;?R4cOU2lU(^w!r}!UD4bZZWxVneu>q~LZKh}Y67_Cwe zaYwSa|C^u4b18jn{u1Vz^ccod3bBV)Ar_C)vB|4?f#RpbAhYakja||DkMk*IMA{KG zJip+!&nrrRt6**$3wi^jx&6M@Cbxhq^uH7c2*4dF1L?+W+&mQV`}j*Le=sMs5hotg zJrVSrQl6dj2LPcl^Kpz)%mn^Dh_AyK%Ha^qWS3_ECzH(NQe0u_LxA^c?3{%#o76~Q zV4e0dc}F%T*i42Th+dmPh-YL*mQZXj2HIPV)gDuxhSi_&-#~y z(=_K*2g&}jaYEHwRg_*U)bd0>?9F|4wEC17LkV1C+lnYE#)916krW?ParILGTa|lC zGxn(Usr@wE{1Fr{nUg(1V~Z_Ojrn)p4gdnHan#Ew2=r*EIEZCn#lSB1Ap5jv;`jt~ z`{g*iz5$s^*vOH=UtBXj;1GysnD1DEvtniX7&Q-gJ~qA_Y^zBnbwzTOg3n}2>K}ys zHl*29yMmsER^_|=KlX+KLPXFR+#NV#iDko(E`pkIkF{Zbs{_-}UGjsUJN*6=x+er< zYw&EG!@!=PH#V9d5M98CvO`Wwqz^Yz4}za?)^V^!&I7t4GR>`IwN+dQU>+ZS7E$3z zvo2CCcAFU@j6e|(oLpa9LvSL!KM^ewSViiy`@^?>{unqBCF0dP8EEin%3_E?Et|TYtQ=tD--_=6XU-v_%To}Cu}-0;+8@h1 zq@{NAE*;va!k&HCno@H`ebTnPS7Wm$Bx{q>xIvH+3)ztflsLCA`<-sy9fuBsYm-@W zt&Yoj`Q?MB5vklW!MY07{g*iN=WU1QiZ*%;)fVoc%ksblP{?=qKA0vVs?PT(|2`!LSfU@5&NV9?q#n$vx3q^OMfA0GKU?AMwnRU>FZ$!VSLP?Q90 zq4}6Km_Y9Cl|t@rj6%+PU_TH4B34jZ{7c-_3bD189ly}-r5u~v7;^qEu8CTw)%+Uo zxQLI#(ZPWjJGggpmhS+|g8|pC?Y2%m>s9FM#ERx41UJg@3*Jm^Ac)$0+LS_HAxASzL(+z-{rrGj5 z|KL55X#5H2oCsK57>wpv&HrqDcOfwqD9i=CH%PA8qAC96P`}#~vLD+d^WGWy$`+B( z);sUhZ4FfNR6ZJ5{7Prz5+_8@dfq?P-~Z|Lypm`A_C`;tZ5t4GH8_BqdQVXv>LVxxgwls%y1axSb53<@dmPCWTpe55~ z43DF}GjvbAAKgueuppxno=JLiY)+_hVQ0`(B9vZx{N#g_e4XO#7*vb6eC4?{t2<<^ zz;UD&C<=B0~0eL z%Iyf3=U_EcH3=@qe5ccP(A(M+96pS1=%NfM4w6dASD=X;eWDfL-U}fMIPo4E#IClQ93dvpiqa}-JLp5Vrxk6&H~m+7$+w) z)?*qvbNp)Dq7u07tMPX)nINA83=oP1BRO3-tU5pS`IH)SRzq!elCSbt{zzt<6)V& zMls|%foqzGpo!bm1SUFwmVxB(nz?Mz)g@-d{>_vGkn^;851|VVYd`R-Ka@Eu?Qve{ zX_8_dTF2i9-gE z{);_;G;qCTYD7UNZjFgxb&r8W@K$)DY*<7gf zm5`>L!0E$7pM})jbv>#+X{`L&FUDHgmaF9XR>{&wKG)*=wH+cnF5fMuF}vLMEk6QS z(*y33H<<%vSw&WJ+VkGs|BXvUA0x?)Z$2FDA!YrR7EmOOF9;{dUoD9Lig1g+{yXfu zXyG?i^%pMsxWz$!jy?>z%St-Kc%ZNRBGD+c*V>nm2K3g#EFLZLZTe8@wo4#41M%h6 z)H(L=hUy_tuKRd>K<_OD9Ko#aW!CPA}K%q+)}4 z0%x-^Io~r0t5^)A!k=*k2$O&IGH&l2Lk~{Jsfg^qW>LH9cHMU95z8FSqC~>+SiR6C z;C(B`ygI4kr6Qkl7=78)?&#T^@|@t^hp}CxxW`^;r|JW5 zm)Iv<{LkRZA*Y8r*{;1Y{rb{%UlrxS2RapuhUDzhNx^$JLjCNn1iydboO_rM^+EjdZfoD= zcBj1~&(`VqDID{!phIXJJ5rKU;WSnIRbPEf)?C*t# zInu%K=86}-c!t=Qg6A6W51sqaEYK2Z|K0K-KCytBw(HD;jw9}(3(!*7`moNrX50|2 z7pJTJ!a4yw@SB?R)>h7Yo^^ak0`j@Gnd(Vx`Ld`Wx9Qg`bk?eX?A;~1tU3YEP*nRC zszEArP8wevKPYuG=29Iss$NaZqr6}qI)Wzzyppzz^c<&GORN>K1`ZV@K(vRdKc|Cf zFPxkAA}TYfZn&A>jUdQ!fwgD&18E2qM@3@6aA=mqBC|ySzlvehc9({V!(_PE-0y6( z<`mdiq!Lqi6F$J1aQi%N2zk6 zIqExvnlsVfE4Ri~OI?L zGJ6k%o1Rq=UAcGDwPwAIYBRK8rzULwj&VG>K=@CoIU%&^-kD8O9B3%kFnCEoV@EU- z*S6)G)zbIYRcLCD$fo<<<>@FNHK=fF>OO|=;V&NRxCj1+p{u+n5?IRzU)Vl;JLXZ9 zwAC%3aWIlII>P9S8!=dtO1m6fPk%CC5t_yB4-T8|dmj?C-M6%8@ZmKYbmmW5z?_TK z<)a_j=kmc+W2wdtpFYF}KMr<9Us+wrDGqoy{4Z$Xenp8#%Z2Wug;Ztw(a+E^b)(IX zk%f-(ZUv7jty?peTwK;hz?x!M!p;-&7Cm3;E5-8qe-AB`xLwn}7#cjI@Zg`@@V$AD zg}J+7!olBaujq`jq9Lenn0_)QdEj5gk|EFP+~c*cRlJ7eU6-g0j|GIPbIjTmD&Vtz zmhOw}XR>}b#XU;Xp<<8+sVe*f-we7Uq_swMd6XDLSMvdJBL3CD9T7p?`cHv8U~qx{ z^vW!4@LsZKgRq#$K*hj3THGl{w~jM-OvQND=IZY3@q~T*(7nI+BJTQH>|0f*xtmwv z6RK~Q6hZd26>Me`zKZ81;+5K*`7bSQ_Uzn}TI=lNSQd74zPtm?v`P>ptECAt^(@R- zhRou}({K{VPSJ*qs=_zl^k_$PkXN*RVRcwFwQ*Jp1YfqrY@5k%{_+eTf?}>_!q}y! zmNTCpm?gme^>0RlB(-epkt_$ZDZS9$_hbVoXF+^KO+^mlvuH+u5kOsrc`FVjmAqno zyB`q(o3eG#ck3k7l{~Y#rJnK&H%lEr9=A))B(7wWIV2{ry$$EE3ghXV1-7c<5U%sI z6Xz0Xf$};9?2^2S05yhVQzY85aY2Y}RX$fz!{BlI9e^V_g=axc*9KoGtJ-*G!am6S z_XQ%n8MAl|KUxkBEDLY=#Ml;6H^`b>-SHUx?CX+z7z1a?+Sb!=D@%;&{L31mNPW?6bjhDN)3wSoG}iblPc>$ zF2B9_Y&Yir}s>&2( zh|G-{f zC`6Q2`#9r8kL6pm3q|v)VltHLv7OLnvfDEXOx=SLLxcT-f9SvyY7L}ZM@Ijc`NYf+ z6&UhP1CyZC3Gy)c5g)@CN%R1bU9LLuYxJm72$lpj-yZ$W=6^i&asbFM2_CyCr`Ssz z{)?wE;OWF9i{8tA57sk!aQpZ0w@j1oLl+ZDK*xzFDoR}`ralmy4zCH+jIVpPhHZiD zktGvT88xO*PM}2Hyz45sh@GZNmP&c~GM4~Yo*H#sisbw2e1)Uy$RkbT3UO0$6%JfN z6il+1_A{=aLCyaAq2I_3G*~hl^b6{S<1><~I>27K>1CIV7Sw5D+DtV^6C>2`1@E9< zCXQn?gpe522Phwe?6~9 z%dSxsRmnQyaW(=-d$A(&j6rC=+4X&A1ND|t^*BKsB610xY!h*yHFuri8P3(+D+%^% z=&t~K^xz5IYCv8rCW(e*f9;q#4_JOj25_s81D1v!=*-wdHQH;~I*hl@kxKjrYs>Q{ z0{c0l)8NZtv{3ZT_Z84Y!YUQcHNH!iq3bcsRD8RRSkAKTrNGpF1r$ecOA^AmKx6m| z$N}z7APAd+(Oe1Af+fKIJ&Hx|&I~XBzBoV*GtDS|Weh{phNYB8eeOYw-Klp`yN^{3 za=IM6UZ=W_Mr;u7{**TJf=|&v$~prvS3uk=l3*Aw=+9$uT zQq-1@MnK03{{vH^wk1JJ72Sdt^N&U2Ld_1Mgb<1`oGGKAzd_&{J0hYSA?W%BIxe@o z5LDh;z43_b8U7*!iMlpDq%s|tcKOed7FK4^N$Btw%l7BiHHO~v%JG4+n%}@FX(m~AQuEC}Zq`vRxMf(rt$iKmYg2~czfsqz zuVE`>g@fWQCiUVM4@{G*7=6iWv-<7EQQQ4w)Xt?rYn)r82+};_wm5X{>(0C*z^3q- z+ONOPvp;uM+#5I(lw@<#gW+G&`)|>s|ANQYMZ8~et3_i)eVvsHFsMGgE z=W~M#`KRC74<#{qh_bH{{Pw5z&p&dnLM8qKkhvVM@}eWci+?uwt&;h(ju$AC3Rkli5De*bMxiax~i+`;cwD~*A? zKp1uZhl2RUQhJ+$(<_IUvM4&7gYQC2glvv+lt_BkQxNu;7!YE&;m+`o(|)>CE!KtCxV^U99$h9#X;=ub|B4*oxdHwg}x$bb#L}jd$>pPdM%rJ`-pItr;^W38vuD6ShzJ)el zVBgMOVfsL&enFr|!9mI`l&4h*3L~QeiN8U*hU%Qq1*r2V(cJ3($m>Vmu^yG2PYP6S zZs2FDv3J^`dJ-xhG9tw5v<+^V!*^E>QnExAV(snIR81WeQ{s0zTFA$G6fdq_*amg$ zbNfHsNt6-CKobiVC3!twsqdj2w1N=Lb`^C^2X~+dyReU$ewS$kHAGm>Z{H0W<*~C< z;VIafnbsL<;;6HYbmkS(klfmO8G04okIwAocrl%Y3t_W+r z^3LPC)+pKVB^yy>K@5UA6x?I)Zs8R@3_UnYuz;g^m{PHsW+yfO#qE@P7A?2F2Dka! zf`3pZQ-DkwH?RduLChO_p(y>8kj5`xSKU@YcnbRkdAIw! zwz*#D(CM$2CJwNcYKiL2hn1W!S-u6WDT>|izEtG(-g`oBJ^)O~F5miZZ~dUMaJ#2+ z?_%%$ML*WT55Ege!h#I`16#7Wp7r;nf|iQSpArrCTgkTAKe;}AnQ?ptL|Va*jtguO z%^$w)_VB{9-JYjnaC>!9Aw*%rM3DU!8AH3{VnA!xnHv6fM$JPoW3>V?aL&?9y!SRv z2Jyim?-T5{gH7qIXOyImOdYtlJPl@V%n(BF3gfsv_n>ph=Pc!@|AvtK-HXoXpRgj;!S){8--_OGaDfW7O*$Atpa^f=l`%b0o$5Eb;^Ef@Bqb z#O-K4jYF_i<#3NB7n*JL|1wj*b)Yq{yqFuH>{dt%U8@-;+}80Eq?n5a{jzjuu~?~0 zKyFId-HEA>{vs)WyfOPVp(SAA(NPw0yFzf-V*Q+FK|#Fcx9zKQ1C{kGkIusI3q;P6 zkEnbJAK4)64B0B9Re9Ow7XNxh(umvenkHV8SR~Yj`Yb=GK|&?Uw;!9_S{PyH5)1t`c+(5UQLS|yfDrA4N>-gj8POYT5u@c^!Is_wMo%%nHAXisX&nlt z#&J=0Yjret<=*=`Nj^^E!|S1*k*^3#MVP{AIrmJN+RxsbBHQ2_^i115N!)w8 z`|G^k6j%x!#qn}H|+>fO%xxV^qy@~;w35B5{}hX0=VR5^7H*wuvSAO}5NKviK{1N3w_^7tB41|DzbEq?8%%g_;{$h5*o<5AMXHB`;U zkgj1ubAR0rYGLHc&^+bzH>#2gbah^q|C^pGM4a#Ioz72x;I=)IBH1AL_%+T2fBL_k z5NugpLp4jGxMsWSg8+{3yVq;xbnHUNUJqNfyekS%i&RIUJEw#CaYxLBw1J#>0{nAb z(E2HTE05YP_;Pn&^9rSp5gkRg0-H&UZ@Hh(g!x70YLAPcfJ)<39U+aL@`tm}CHF_{ zhqhMZUtWfK%8yW)LX1nP+e5E!f52{BM~_=G6O`8qj%O9UUM>nn_)1Zm-itz>_I6+zb$YTDM9oQ&fY z_g0k)1K_gdlVNY9jLtdlTcsH$@N^@&QI6aQ6pcwJ7TNer;6F^8HxwgBLJZADiK1 zHS?8i0rm)akOG2S5|4#o zhA`~^v_^&k`()BGU#ZkNo4Ne9|TKAatA^HHpcLNVhK z43VSYyxj9PVcy1v1I@pNsCD2q`}D>=gkK%oj(Z@^A%~g=n93BOfYd~${-M|!RQ(#!jBh4pKq&s0jGv?u~aU~N=DfM~@zL|;q`WlD5yKHp#jhp_}W zqR;v_4FdZmjZK~vIjVr?8VJp9)^_?RQ~x~nPMz2GaIO8aBCaW~j;g+SpUrX-cen(% zp9wR5dNHZv%JXM6XLrh6-@A9u&A@&e>R${ z)>A^({3*rNxeFm}Wzr&lo%?49i^La-maz8$jYsZX`r9-sT0sFtdgLtfr2hUQhJz8? z&{*g}k({N3m;buE*lLl`%0&~%pKSwMj?>FliLC{Wp5QGPYC{e|8^K5IUkX}6$H7b4 zVCh@CSe|v%QH9G1(5T=H0X}$V3TM-0{|h8R4zRs|X)+PAgoX*Gs(XIl{@P1kJD&oz z(g?KM9BLiz+A48(jQ?Hm3m<|ElX-5uT8F$TT!5X(!W@P>vfEs_hwF*jdJr+T_}M(j z(Y$^}gG~b-$RHDE2?86`n`xTMsBU^%w zH*4_MRm#YrRJePF{+SyTMePBvq0--=oI_ZpTAc;T$Yq3V2}Q#AxW9dnT~gR8_U8>2 zxX+WCTY8Bz0?L5MCUY{ALE%>c@w$v(!jmZbXt4hN{M?C9hpF$*>8-e|K9p>s0^at4 z2fR)=U1^9b%>VlHv8P3+=kXU}Sk#g6FKtviUh0z0h81SPlc%4c0I&=p)VYcoe#o7f zf5gj}qw6U)%=ck-R+fI1roDM5o{~;f1Gk52TwymA3^>ya>bcRq}xqdONk_`WM`&*4;E);J+=j zh?=py1atLmKsM|)12wEg@aI8FoPv#PBsx+j*xa^EjLFJ0QTtlKp`?6f;j`*$l#mRs zKC-RD99QVz`CqH%fB5IIKC3fd>j+-Fz15EtJ?rSZ>JNBFF*uvW~amp$F~w-3W^``2Of1?tdc zlUdhaU(cg@SJsX7QmEarOU73@-Qd!03!@pvFqbY@O)s|jLRyxDDeN-DydQJAdLGhD zJU(6bZJtfL@cd_VjIaE_tCtg)3g^yoyv$Abpl2?$eT}#(YA?^<38g^>2%d}C{>tA5 zo;vuogGS#RnhHfAr>kkc3?V-MdGd?;YHKKU40D|r_>|X*NQ5Rrd%;$}6P#WwO5Kv8 zhxSvQYb|;_{+b~MY^bM2FWz}Qi3ZnY@=$ao~TO4@^EZ2cWE4B zpWKZbP60FUvDJjoqE9f~4y?=me!xzZSLfAPOi?eaKHvZETa?$%sn+jhi0K4zZiXnD z@j(m6Xk#?SDd#K~-#>tJn5bYRPzK_U{d`_`xDDrpyITgr{t;{$T_i7YHcY4C8i=-q z$X>D=(-ldcLZ#WwivEZvR`fgF`p-||BpE`2S`5?nly1iOS~n;UJMm}+CmW|!VrWQ* zeRF*1xODTY2#I%GA+O?3L+Fi2_QCm#%tfb#B~EEM(!YVZ{U6W~;;J!qVc$o=bRXW7 z;|76R@Eab)HC$Z@{A)B77ZhTy^3MoISo2U715oUO^92Yf`lF&>8H%SUzzEB`;}bJpaw=3JY6Gm z7P49f+u_;5r8(@kv;f?f6!v3&5(%E8)C(uRhQe0O^NmASgp5S`2oD_pg5)x-rEtKg zUuh?HvnH#B1BT+rCZYG2CjvVO+(glYujb2JIlGp=3FBkM*s(J^Lfb&zXhW4K&`n#W z?G9wJ_Q#^9uIH+p{hoV%i>?KAQYy2peW%Rc)4L5-Gb>jdvGUU1>N#@#Pr;VuDxiC5%vAnMiKC`M=fOc%5ICpl0@;T)4h|ED5vm~mU2+r=T*=(-OnRB?*_FsJCrT+gh%DyC6h?-S+P`8o++s8p{~nUcHb$r`%nT{EE?aPsUW ztJyDrJLr@WO3ZKRS{&B#JARTdkPZ60DJ_Oql1dzbF)wH&?bLBw>1i2#Ml1JXst{ZU zwZK5y1W}wrTLenHz5ff-DB_d-7fJz6Glr~RNSD?Uo{;+XTgeMX==#+}Fo+J$1Ajs@ z9#`g$*&Oi16FmKu8WALS`XhUA@+(ndu2Kpr8eH!Ub=zH1H*HuL>%q<)bWuzcb zHlb?8O{42`ZRQ9XQlkEX6l|p`sq{HE`7P+p3Xm5s<)-#dGsOuZ718~eWy=SG4m;z0>^KPZVese>JgiTg9c{f;%R2EF| z2wHyKqDbCjoOQNA=wi-;vvU)J!$?H8tcc3V{me`SGu^Js2K((uB@_RN;|NrO)m)TH z*78m(x`vyuxuo1(ahu25MR6tx$}G>DO61jJ76`C+iKK(arPr!Arj*`G;6FZ>azb|}c$V})L|hFE^JvM}%YO~)y57JJQ%92P zaU!~1rwh*Fs10EN;m;!v zbyud+R&J3QjSTYgtZtiNUm~u7Vek!f$hb^($ipouFWTfJpQ9ig3+ArQvgcf=S%IsV z4x>}`4xY7b`q^{4BoMb^^MpeYcHvyc;SYD3A`k5DQp^0eJZt6Au!%#>A5-sd8PvV$ zLx^2(!@5qVM$3g-O+uSFzPx-kPQLB;I%IW*! zFSeytyVnF;57Zjg+pxpKf1jE&2JseTJVS+syV=)h7vyq-BRpZc-pq8C^0Z3QOxlSk zpL74jyns5jCGzRbiu@|_42adDzCdwlv-C>Y{X`{4bXdMe;D0rA8j*8&PF$cg#3p+x zvd~>LOv=ef#b70ebavhJPA!8muzji*n)6~u|5@C95_1Mk-u2HyN*slPdhgsFP{hoa zLkhUZo8){P8f@+38oSGvEXhPBrs4L?O2ih7Wax^cTFzo~Lo9|{QO+R1D+l1MTuCkA zhX^RUyQNbS{8nAk&E{yZDmnEyuepduT}sn3`gAEJG95U(2ObP<-$U0?Kzt|9+dX82c%zjVR{xb z-K7_J3h)*^hZpe9$&o~s4Og;m_SJgI^%8t+;7)83k9DsExK5;>`%7j~4H^(xgLn!k zbHxh>{+2+8*PX&&mq;hZidBTHPA`s?7>2+x`761u^ zx7m8z1?#vq`XB)BgXVkM$CGM`=|>*S$Q*CCqE#ZBt#5m0W% zY)NgzR48!>-W{8!H7cezyq&pXy8;lw1iOCjma9}3U;fDFK31pliq2Lfg2!O2`w^wF zEp^)u0Sg~k{wfKn0h8!;mP_wUp?Cxq!wrS{MW$MZDM|&_O_3WyicFqMiJUEK_!J!* z1Kg*9BG8?6qW$r4paZ`Kq=SHt9kqH=GZom3h*>|q!dnt1d79{TCJP%T7a#FAUUbz^ zj0>n`1{8&NXWH^&h(liQwHj1OU0%JUwN5iz;G@sio{QLq$ez^IU-KP!^d({(P5}8y zE}C4m3C;k=eIsD^x44HtH2YeF)y1HC(EW^D$fqt;r9WVG(dBGK)P|dKqMj7`j~@I| zuT!x^-Z6YPXGRM+nq=Na>~uCJR{skhr|9QqcjDH<~qdbRhLE#Fb8 zabzHMY9ezCu0?V&kM32y9&;^5%6DataXg{bII(xF%NMY-im)o|=E+=2(IWc(2aN`v zN!&cE^Zu>kHbqL{@qV}wlY8@%VJ5Uj9XPEy$*vVVa0ko>Ti_^0<#*63N~V6*0<5?nIXofQrxie%wEPTRSf^Uz zS-1DuWJT5sUKceY(<&!IN6l&}p49>}H*jN~ef)kI6YX{F+>-udW+p{sG{F^iTtxr0 z31j|8?LrO5;R*hc`e|tSn8TVs?7Ld@-m9L$Zlgm&np;Qjtp%0lL&ecZLJTovQ>9^} ztl{ug83V2Vw>;xN=;qq(dbLECoxgeXT><0JDyz&2@t09OU){khFb48R9NTdfSeu)0 z(chZQ(*j0%&Jg|W=KslP9Y?%*gtXd@ zAX~GA0_RW=3{zmQ4^zDCqv7O=Ja}7r(n-m_p!0?09DN)Ap<8zhR z5v=Y@Jy7OATkO2iF;#~OT!7t;T7Rq>sSYtnAb|#1%IDQ|rj#~wiP8_7qMIxIuP%p| zEmL~4DF?B!3GN73@5$r-#-gj5pQ(_hDO@9v*}H(SyJT3xFahRRRb~h3dG2wfBjqnA zC4+-V$6sK7pZ8U&ojI#a^~itm+@Kw&P${CQ(>QsE3J=-QV(vgu#E@L*&p{i?;UH?1 z2UnNb{``LgL=8amK%~_zSPI5rW^y66Qc4(t2N#P%a5*7sW^;jV!%Dd)S+ z2Fc=VuKzo5Ivb?3tqx_L?bFXJRd~3aa`O-j@4HaVp1{-8r~cs`c?N zIb>RKl=UZ>4iD)$bdWLsa)WV&nzAmw5Yv;xFVgsL>ufkhq6PPrqQCg3b zwFO*LCg$lEB_B|y=R;-r&_(YFaQ4e|*AUIxeEJjzvSQ{x8bMa|r4nQ|-)Z(;id4}* zeR}S#Ez~y_Soe%fsR$p0LT}urk7%EM7P)RPgik!|dvyByZk~nvuwvv7;5kH-5YJ%K zNS7`&p{qE$a8|-r`-!md=U@$NgX-sX65iQ)FD=Pbca55|tmkO$ zV3c;R=$9hf!dg0l7)-yw{9kl_93Y_jeGTwrstEslx7eCmD!%%r53C1;eEKj4(%OW* zMPq2&FGXnQn6~#Jib2Uk5dkZ~I)(_jqvS)NbE8Z*VwsFPO-30CoCM<0hoG=i`0Np6 z*nItF>SA*tlBl!+sqL^cxDAToxu*fUEneLc?KvC#ZnRNCh$pW3EgK0gyx3KUiQdFk zx@LecY{pE=<$|4PxGMDfocu_xTA)%60fh=ym?4+Hud`0C94`|iH1F*L>I?0HAAl9G z4fe(^6%AF!=k0l*D+LLuAvccIXUQ*$vgPxor|OlJW2yB{QR@;_u+Z!l>Md+Hb(1_0 z$mBwX^sheeV#y}Hpcg|I%?n5lm@T89pJGd6EVws33<7J;q$DSmY!lwmqW)`jbMWFF z20hHF9=b8e z6&Z={eW!;Wm$yenWLIgp%X66u38|JdK$~$suBg@bkum!u=-{3IT_}%+{xcLs7E*1& zj_1vyvHoWSlPHnGMx2)N95> ztYnhd3@~B!>wZ!=M*qE}Aw_aHziwr8v3g7VL2L6F3zm-a@tOk5eg-$xO=8m1geU zM^`G;9Vp_TrJDTuGVn(>hHq}$suuu?2 zlxITnq=1?;&R^*Ty!^5Z_jOhAwY1@FMb?uiuu3fY#Hu;n40xf#b^Me8HL(BKYcO%*eKJM;48JMuZ~sY{%~J`OaGZz!L(dJCpR;` zZCtL+-DO`*qjvnNfx;J%9)?~n!mhw!&i`7;C<{HuE0@vT@NzS%YhMx$uNC^cD8&<+ z^DKTgv+2TEdY=WxS{|4-5F*V|O-s`Z^wJ9?`qTs&Pj7$TwPlE4lJlyXGtkALO0(;Y z>_s6wn{}8QuLe)qpzPo+wa>W{8m0Bb`qi1~%&7poZRVHPB_-Y2n+ZzWo~~SZ^f$M% zM=;tJ+G#Wiwr6}-WC>tMHg`+!r+!0Sb%hmbNnJ#zn%tssG!T0+Fb1g{4X*CJHL$Ez zW`ym@5KRZP6{I#OORmoFY+W}=$UoKHmktqh-+N2xf2E>rotszI$D0c4ZR$23^Pbk&!=E{kyk;E^{K>RFF>f zDlgn0Zns8eD#WBD9isrKsXM~v-MxP*JtdGE>G zY4DXMOO&O!g&ho)!{KM-eNpyjgQxmBCmiVR3E{OB?`?vaNsjVAHF9%)pP!j}kSz`x zImY{0VN<*Uh{a!%%#)qdrH=#T5VairL12$OpGoJPSQM^S&_A5x@^yF^NwP?m5)4jO zj@YbocNaRc@ga7~y@&SdgP-?A&-{Z#DS1VTqB_R@$KQj$h-njA-)V|J4k@A)f32)g zt`&$i!qG1&a;|pYV&%)r{%1` zPLv`#sKnXrYWfN6Ek){-ICmRLNn014_?Ld5oGyAu7aRMZX5TE|N9`;n{uw^^Wn1C0 z34!Q~Y7*0 z$>au+#J|)o3Qjky`)jrDe$f@tP-g7f*XNA;S%2$k))@Sne+Qx2WYbxiP>O4Sf1f^u zqbW4hbEf#$5GBz~*#iwR4q8?p%g4`viZp8Hm=#kmf%$edJ%V#!AVe%FS!g>~Y9_0v zm%bOMufW`?9en{ALpL>#1g@nOSWgstG;Bkk*Q&x;LQmAe@+;0xo4Du9*TMl-;=@$Y zq0fw8*fHQz=+m!zI@g3l0%l7doeF(DxWj=iLYjCx+OlsI$55+s&4R!kWs@``$j7cwW-08UsQ~X8yc>n zmwFyZdA%VvU($lvWl_YT1@2qYlr@O{`E%s~lHt#(f6AK)I5%_OOQs%?3w3C}b4u;c z4bnxhkw&3&VCJaw=*NWNlt*!}N#DeVuhqrPzz1u=Oia#A&t0y`KKH5P?y>})Q*V|? z1DD+dzs}I#T1mciQU0_lVXL)#B|1vTwO<+dyb zZfMJ?ZCynql1b(CZXftBWI75yQGKr5<>x`}FckXLIM#|Aj_M6At<;U1bEJyA9o6LF zlwAf()VrA4DldEU_UjB7Qm0NmW3d;iNx%v0fndf<8LPGOpJ`{@@VFM!b_mM+*VXj% zhu917F8iQO3D&rAXq>j8QmPf}YAVlGAZ9XkAK2O2n7V@*g@w!8;PCT{Y2y&DPiNo{ z-F#^Gm&O1KIl*aa0bcpB3Jd?Bw80iq1Af)?T?MW1b&Gv621~t2-n_HAh>?q(q8>6Z zs#*i|$ev)YzcYL&(F7c@klJpln^C)vLNUjME!sFr;L+u;_5*b_U*+z!hUU#*9+ z=bK9tN6uhB;aUdmt;s7aMs51ws)8~_B$kco4g_{z0**qXcn>>ocm5g_e2sQ z>;v~OMxKY}ss2(4NvU`#w$-VWdekiw5Z+v9J;)tSnM+FTAX z1Wl?+XGP#;uwVIS(NzwTv-)frSPl9dY%k6a_`ybugf2>7yOna?lXXZfd|p?*?(Q{5 z)doE)`12}*cIX%wl#hIuN6BxT1Mh&7^3SHD%C1We_vV8tEJE3^NU?WBlMnB>RyCTP z3OBWKPwb!BAxcr;e!x%AvK*D2WKAw&2}rKs)vRJ%j08p9e@OjdtfU*%7CwdZUZw($ zJnLzdl+mv^wZqzG%K)JWs|gt-ww}53ojUlZ_q~kUqyes}LWO;Cz9Iwy z%!n?r>98!I)f;*&0Qr8@YtM}5#Vl63m!uw-- zEF3Zqs|8;}D5h~Ta-!1h*%*g(@?zb~pzaOHBVdtOtDTgt{`$$}R~g%&s_7D@Q7A)W z?K830&&oVp+<>dVzzUj`aJ#g)ODs6_4@z~JuhP+lsnC`%{FLw8x$TJR(E!q4N#c{# zgJiL@X*QVUlj^x5!Lq85iu`nt`U~Qdd(gm6?29;jzqbwOy-W+yjQgs=Azf>&fr1{I zbSX$ID1r9*>K%-LX8H;fsQRE(g81Ap_%9a%YD3qQ%@W^835i+tP3}3kp9wMF->&@K zE`k?u6N{%TSug`06dtDr)u;+K*ed%B%oZ{R)WKIW9#RFld`0F{2N79r(aZ=pd`{hd zE)(Ow5yDfYK8|ZM!XeVTkt@p+HA}@1AP|v_}rZpiY!2viRjxo{%e0RrTXW> z2ho2l?K}PoNj3x^D+mZ$3*XK7WPNoBA8^yP>?;DK8$RjXba1 za{z#9?R$R1gtUllA0n6w(-%|b6C=F42HiHlNZJ7GYUM@5tiTW#=a?YIab){i~)fQ&+-cN(W`VCpwOwyHxAd<$%57=IZn%Uty@vb3( zK;8D8LEwZF?>j7GiO*zo2QB6Nw0(ptfu>~TCv4>@e8HCyXd~FeEcS1t@{J`p1Fb82 zr&}BKi=Sc?lMf?dzl-M|_GPxTUMo+v(e-H*A7pP)NGWq+3tH{{#NHg3DgK)UC;aK# zHoq@(5+%9o`M^!#vX_{;{R?zE(cNa~Q`IYt7Ydn8A8^I`QFi?Z6!7EXp4)})757t* zlSuYxKW*rRE#Y2s#kt>S_9#i)7ulQl-rixOhI7kIdjNz2#qo^7X~BSkVTA?WXn>4) zTyqNz$9I1$-aaxS=8h`VgBs_#D78gg-)o=c+<8!rtJQ7WDp}To07!6$n+yG%{eZ1z z3spWSwisd{iK^5Mg1l({HLqQ(8lgRyluy>;ZRz|xxjV{IVx3FDX_Z^fWrZ&Ox@O>e zr`u?lp&+i~#g*KIzWX^RPG>j<+pSwC=sIS5-X&OkI*n_ma@T_hNr}PNNRz81LWVmw z6G-RFi`G3Wh?T47->TD9j5k5s>0*y^6D;z*ER|95wtQ$ac=r2q87gLjlU7)@c6~Ep zt#(f_j4$6!ohX2(E7x62*qo5_ZK@ryXt}1U0clvXZr=tQig-+9WS}qB2eZPj+i2V` zsiwD)1b<)aeoOoCoCAE=u@<=#(W@8m-9PmeZg$TNKeT)U^;|EGJv-b=nj?Z3{iVd{ zK$E!wWCW*MhnqM5?j_4`O^dp@P2T=-5u@#t+Lbo4$W_}Y$J!LL#k*=yP@m5uR&ov#0sA977-uyA(vBb z8uZUr7a`Nh(KS?M2^PDWLtO#uMt(pGEYwPS2PNZrJe7$N?r0n1F$t~FJwaLyeaB+(abo|4 zp{=_=K>mNxDBr1M@j-`}SjL>gJjz9y2$Ire;e+p=+8Be6u^X{NF2a518>$k%r^_y;Ol80)^ZIsUP z4*OWzB1!QUcobq~?R^i2H`*^am|E?$N1$VxIH(;iNy#t#5&2sXq1s`A@SBokn zF0?xF$r>%)Sy9@ATd{lsJ8gL73^LBT(pDWR5$kpdO zNwu(lX&_m?4*M)(e)i`!widP?pOdj#=5Yiw7cDGOd^x^5`N5N0!Q@NN<{x-z_N&K^dB)E()L*PuLyu5II6C(4no#}&_3m%D+QAa@kiW^0OS41rY-l#M% z1&or=plWU$TnCGIbj67W=h1_xvxPqvKrXSDoe#}9XJPtcfc|1Cp3>~wO6}5PVG77#qE!}>nTbCF#>x< zKl%8X=96Y|NZ-(mThRupkoGKh$C3rYw|m~dJ&!eZa!IS}G(NqRr$}+1w?XJ(e8wlZ zFSY@(SxoQ0FB@QwLm|7UGhSrnoxNJK&*aFHd_8BBZBp|2UU|RCm2OLm=@S( z$Pm+%5j?+j`J-x^P}=>)e?bGJI78C{B=!oEyTx;HowHn5qQ$!3mvO_N;)}ghN@!x6 zBUg-Amr)|BY($nVqDVPBTu`RL8O$7O9*%9;dJj!iSWLZq_EY!5 zkel*Lw2nEhPs2^b!Yn*s#kyf+n=%A*p5)3&{~DmOo!Dl)x|o1X@@O)fpD+oYR*ey~ z6ZY1}kxSIYJE_)MBYQ#kRiZfth=Wt=SgJze-K&4q;N*9!rlqV$urKC3NGl(t{o#$$qntU+cO~dw90|A zcuu_{?rbpqsjt%G1D2pD%77Dzj_4cAlMNrVk92gzq!P9{@RJ})Irj#JtF)SiQTB!X zz+0l4biM`SpFI7Ah_bSgBZlX}%5&4OV()M@3ZH=WLpFxA$_5QeHa~C$^|6+!u3}6z zrer7t5;z|DL#*Zw?*@O{UF>GdpbyX?3900cC|j8#vjb$20;j+=LZ`_xZmT>cd77Tv zF~Q9N1D^vWT07(22AvT#Zdh(EtSRhLr0_lZN6N0gXxxoL#52)EO13u{7$-VnC z^vKgx@IlFc;D?QA{&^nWXVjLhjN+5m^H7$(*<;Fk4%{Y1l7&4M_F#qnXY5%4(xW9O zr2Ub6Xa3S$wK)AJU)c(yXDaM%9Sb*9LY_aW-sz+4jB4x|gfby{yF0l0@ zsWY>Qd&^87#V4kt-$3jCU>1fl66o2P^q!$wyBA<<32k7xiZ8Vu@IjrZ6q~^xy~XqS znhYq*r=q{~Q_ULh;YSxWb?d^FS{{RcygT>Y+1ej={OzhC=#6%-KJ)Uk=}*%DX-7T}(=c8vGspYd)>- z9hs<9qdU5CUfkl$Wv{KKNj?4CzwbM@a{G-NX!I5d?S?gLO_yWn?+}Vhs4RpUBCjMw z;9cgutWH4Q0{amKjc?xTlxcbE!PZ>b``9#{8S1?Mq>)e=@Osc7ThG^mu> zTeT^?EkdqO+$S~_RR1~rGrB9Mev##jP(ZmA`aeCVQAVxivk5jDRo=a{xrSsPB=X#k zrArYL5T(fXtS35qDEBb!u}{SH-G2F4{#LB(M2zL@e#SUztlGIpguMtJpBD5jQqb+d z`r|DsW-`zB30#Fr`pS5k0T-@J1^w!2@W9PgT`3|?8y>2r#ad9LcjLcb@@Zi%BhN0n zo|ZnUZ4)hldjqN4Tud_frM|N2H=O&_R4jSJ2q{uRe04aj9^wWl8)b%)ZysI4*%m24 zT%VFsVIkmoohI%yO1nj|O3VuN>thZ_BxGIiJq?eYy;=Jwv*+fw+8#Fn8+V_1^Py*}w0g)MJ_@oS%Ok~WS1^{>GXa<6Gqj@kj{)`FLIpYlgvN?+QlM$ zTAh-)Mnv+_{Qh!MQRUlJD0y8%GNuD`q=RMR9ezDZfX>EVW$$*p!G8_36 z*nr&rP-R8Y&nsZ*va!$H<&4hnPp^Ibk~LLhF1OBr2eb;h+?KLXs1kcagVHfCoS1{1 ze3*Z+0a9H4h{R|aF-PcYQxqYO{_VU6-=X=Ig*F`qyQ_6I<@&pCEU!3?MUv#Ya%T$W zN~zn->kee^q|2wX8qDEqw}1a`Lv%>y*1hTT&P7#AX?H(0N|7I4+vsWqds9HTC235v zaon14i8xIE7*l?JQ?6lVMZfH);WE4y_Jq8R2#>ckY|F|pP)bN~@K&&xt~~S)>p#Va zA;}Q@%6j>w2v(6-tfgS=(o1EBj)h2t3nHJVQRmczNX~LPW35V^g4G?_+<+#rq8T7w zj7$BFmD46jS2C)c7fruMduz=e(@iYvyLVdxmifg9-l)1Ph=!)OAyjSg7xHGM;ql*i zms!~uY_-a4Bs&MhE4ADX{yQ$^iYcvWb5)+O45#*|^t%_2vt?inZh8;TTXqaMX?2h_SiMN$Bg&hv%3f#a+`8#nyV%yJHs=yQT}yoa(h&IUJP#Q zdFLrMO*K~Sp?*k{MhnI#iLtKmffyZRL>2GqUGNqtyCA+w48(iMs8P_;wP%PLX38ki z>y43no~nwk6F%l0NuQO_e!O}=lAj^^&;;EIIqe{4sX)f-vPNv#H_8SqP1(~#_rs12 z9>;Ec&j1eCF%jHIK)!3wt822~*#51^CaixOYr&qIe-j)ERv{T*Sx>vp4~jdi)|PBH zxP(`h)c%X~)^%xp$N%h^IIEpPYtQ(eVj=S0twYD9?AP{D^+tvLzLG*MKvPH{K0wY( zVTP#vQUvKYSU}UM4G!ysJ9)*&SWkA6%5q(0a{LoyZpf%!6B|f)I}fN9>VXnA>@F`< zhjwf^=Co_KE$iwX9dTsa@js6Dvo80nNy)pXH=Ti0-bH5)*N}`zrYko3>Zyue2^+jAq{ra8`{1|tD}YsLEEs-j?*BzEDEQI_ zkFehj_VAI761*~LU*~~+1CsR0opBy3St6%>;+`Xg3za@tfo1f;*IP||C&??%z1a2k zAzfpOw(J0*Ir~5NpdFgPQa0P$L#7&P+XLe|wn|{1T?>Cz7%cSk#h_ zwT{!j5|Yi+b0F8tPFU9hNJbK6VoR`Z@Q{~9R)GRf$C9MPEU+D!%z=lvpD-^pgfW+& z*d2VRSy$Q)?-o^r?evU?h3`NG&<1 z4ROHAN)+&qR6k$kd!=9=8Vx1GMcc6ruqS*oU1T(Z3&yKnOM(%cWX57xFZ4S$ipX)x zPwpOg(LTn9E9;;cHWLiPUuJY6y8WNjAF$BTMzblekw#tDi37o`3A7>Rg(~d?Es;fS z?YxGk8Eb+Tdo{UpD>V5TBMjHVKJVma12}a8TJ`TSu-3kKSbcPzTs6up$1J{vI29_O zobSt?_Znp{FgoqiM%;sap05m%K=DyN5$K>H$!+WBu1HO25EkPlFdo@mdQ7elJT9hA z2JGR}9z!w$8mF&PAh+XAsrb>ioj-AtUJ^wda)H{eis3cp(>}m79eQ5q-(TPq`1@Fv zlIyk_9#h-6R3w$=e@K4|YQ8t8>p|Z&{#nCBw!jTg1a?ebrcf_2y*V0IiCvr6cd8AX z1U?MNyB%d@gHc8U82<7S+q}B{G)Q2K!O>hE(0hVbVb*QS>Fhg@5QI`L|DykI-z&KJ zKT6<}^fX4qj%wOl=@`YH) zdI@Z6>JgT%n%r0nQOi@<$*>xY)jSmGq&${dh&2y@hcdJE@$Ja%hv)V_gd9;u6y`Ze zm#`O)s0@w33#gq3zsXqL>jkMy)J2s-<@N$zyrSSjBj`~$*u7wYyzTOy)@}~$!ykw2 zwWPSsxu9ePrFReZGiuwmnXmK1;xzUPTTFmWXs)dNi4*+mOc7Q+@)UFtYi92(Rp}_z z#7EvTtq=XmLAG5KGf2R6ll-KrEBu!Nyv7otc6iM59k9{BatfjCZzpKdqI2Ni{mj{q zAV7K0_;1u17-BE|hb^CfMF*|Yg0Vce#YV^ohs4V=%hMH_;*hnXiv8XQABuAcO+|YY z&bRiMiT0_oy~iktaJK4;q|zH$RIZBD6QPW8Tmw(B$NX=sU(U@_7VL7}m#b8#&%0UHx`SpR~jp>ObIjmjF?N=VKU zjh|7n78K;&JHB>-HzMj7zE2!T&X&A=$|qorZj3WO9|Y+vpRb75n(d-c~5GW z`MT1V-Q^yh-PeMD9ATw|a|bJVw{pE)f54kQ%GwuyL2+VoEoj1YNXHsQMA8i<{0aN? zL3Q@7Qn{{_MHfOhKRA`js`|>%Q*sMya44MD5a3acT~%IWglgYk8};HMpGcV=DkGgTEL$NbhtNseBb0(#jAxMD6s>KtYpiKNJDxz#+3MHF~EQIQqXXw4K zwP&Qr1NOUbKhUbmdZdI;w(>g-FGGnYZ=#O3fx~MeW<;>6W*wWDMfrb#GAWUXTlN5p zJQcPfc|ZbmV=L|Siscx+bU30K|44k z*v_JBa+@mi_;2|)+^w5d!^N2m%>;wZC68L4oICpbEJnQ{G;3d|=5@m*IZaEh#p z)9*2P%Q!TNq-5#(B7XYBOBI#n0veZfYF@}=2dxgh!LT1%_Jsx^t8)F{0IBUToD@H#&do$VmoWa8&0?JxVhv?p0Lx=RHy$T>6qsK5A-aQ0V6%pX8L6rUln~nXhKS zQWa!WPe#4vo`^IGGxW~!t=9e8ys>IX0q+g^(FDs@&dhrGdp88x6xJj;A;S9_nN8HK z7NA0Q5Z||E)9;rV*D^#}J8rnRt>8o79_?4wSmq-ZYpdg}3~**^sseoqmy$H)EDo-8 z3(riueQr2#Fx_Etg7MLZ9NdU<8_3*e6AEe36L@q{#QXQp;RT30Sd<%f+URF$9*58V zy-Jj#va=_#W5`INia4KBWO5L(O|KuOZMX~Do>KUUTi`^B6MFq@BN?#m-q=x9PSPrD z`t|9AJrvWzVQI?RO5X#2@4c;5!?{eO4isJ%?Ge%+7+3gEB3LJQr>zwzQ$2k~ zz~g}G6z^lexRaarEBHUy0pkSspxNUpo}!8!W-O{3G|)yJI*(wQ(+p+7>*ro#&f?U= zI=FZO=^OY7J->`f6l59GNOnOA@nA*&^=U@NG(KkGkO)?Q4!i&8g{luhq6cU& z-QKwzVt{`Ql^3)Z5*I0SNj-Y+^(z$x*dtOMxP$s!7pnuG18{}|yZ9Adak9wgixwmy zv~Pd!dh+%oI$J3ZsoZs3(sh86&|*LSQciM*mz-@i>+3@1Z8$?+#eKKpd9KV0o_QyE ziRy6CS3XRWIjW?Kd$zYp`&v48+?cI+M&AZznrR6c4p_4A+vp&q-m+goHjo;Dm1X%>11!FGZ4XPA9ZC-bga=-j5DMHV*{Yh#e?m}?SO>pwKeE4&} zN;337vpWBC`rO>Dm3;}R^5#UwlW>u@F>(W~<3*4Zw@Uuyu<8}s39tREto`DYl>+&U zc#tT|6AQ@^*Ro*wNRguaX%jr6BU=)Kj&lS!4;LOVI`dzRzp zpn7cb(ATTr5xvl1jxFcjh7-H?pSphshdL@5^52^|c@I+Vomvw!O5ahP6qnFc)@oO5 z^>A*p)7H8S(|c+?zBjUx32XL^MIW$A-mW7S#TYP6m%P?D`R=^Gf2|IbkflYwx}3CX zs-rBG>Sb^E zTwnxh{_xrYGzY-|>gjKC7v9Zc;?J3m?SWjj3G9>ARcxpFx!^v-{UCrDlsdirzLbH>ozD{LR#gc5}ECsf47cv)t^}h0_Qfep50l`ta%W}={BG;?fhF-E%e z^N_4>T4rAR#yQl?87vzleKk`_X%N$C^3voDpVaT1ErfaSU+xZb+_QXI(-Pq>89#bA zW@^v>J0cQVu}nT;&;8w618St;4jWOy`PrBYRyDm7+kfvJln&~zJbWTT!*>C(UT64$ zi+)PlWDZs!c`xT$f`j`ET9HF%guE@LVQHW?{iK|3S1CHA#D9RMy}Oo5p;PdUa>hlh zQK37R7x5a@#zZQrxgLd+3d9CS+N1>V7Vunm~+F{cQLq0f-U z;W|WT7|cnaWozZY&sJ5XN{7yacA1M3zNqxxYU-(XN|wd;R5pfC%s5>1X#_zDnSwDs zxj|=u5DQ|3USCop{J%A>1m-HRWdKry_#RkiHaCK1@o>evJm59C0hmoKXv3=HMYgeM zQ8g(Y3p&W3kKL3AZg4(sR$+qa{r?y<;G2BVB8PTOlYEjSTB=-gm`0yzsrjG6#yX{{rqcwNS?}@^=x~tA zWin$v9IU+3sq&8etK>+C_L`gc#C*#y*yqX&m_H5{&X6T!vvpF>qqlg_k`bK}uZ^Jl ztSk?!{#{L{!_H0W_O^LXcwY+qhe;!&2F&j3e#$@P4u2(hsYF%W^YE)ZuQv89R2v&f zs1ZmR4kHS+R|nBVC=dHNgp(ne0ts!B$NpFO&Ep#5Z5W`FU=51Q%ocd6dcs0q!yczp zSAKPKw4)VpRbA3X|Hcmb?QwMTFhn`I&esnMSY4~B360}B%ka6##&%10_U2>5NQX?F z`kOwu*|`Z>cywvW8>$1KWIP8QSF2S%(WO+OR-8K+J({{yn8k)SQ;V=%#|5LElYV}~ zdK`L?(&qV)d{!jq6k^_yYJEuiziHo)`B=t7t_a@*-3w)kiNi=JngF((p1_SDACzt| zwu^&R$bHEsUAf$*_>KHek|`m7>$Q?_wyY>4%e}UX53(G4tljXX2^$OGhWB{8 zgTB3hEZgxi(6s={e>%1ejSK0+>-=f@toMbMRHd@=kdZ)0&uI8 zynG@5-4c-h7kz5eGy>6BN%4Fmg&4Oypic?l;ckp!b}a%Fu3-WdKrrrta* zruL8jzwg_UJ?W<1AcRnaO6eeEA0$O(BqSk~gqDjNB`qUlYZ(b4HPNO;M_Q&ujkHV) zP0ciIQ>JN}Iy0TQuJ8FgzQ6C|@%y_!syS!Qd0+3>>-Bv3+g#xbOSXivZL5Q!=%{Fw z_cgB{D|!6hhuX#(g$tK*fi$g;W_cZzobR|t6SD`}LWI%7#b`W5df|ngBZSGaj60u}JC?c(DXMDT4fF)hbo@^<-xmHh4Ope+_|a&H zH)Om(AlcEo-s{?Gqkx9(e`c-eWj-$WF&@>HE4%SXT&2cG#h)Ua{GXUf4n^B^_YU8+ z3-Y#%cUpf8ypptigVQu*bkR$0D!cGgP_K#2;i5~OT2*r$j~`Dj=v00ZzKOF{4@=`8 zh~7a@iNCype)x;ywbC)0wcf4vbXRf@{-ytYYV|la1c&dKgcP~>w={~%^}MNI$l-&c zyHR0}44Ibq^wj7p#`3tTsxccuVAWJ3mOc@KPFV<__kgomLP{UBxy7uhnRtF?OYhrE z{F+A=)LbQ&o49?f0-CQ|{&n=lz34wCoojE4@Z*$i6$e6nVd(7P;vPwkfA z&aDKPv1$EpO7R>}k8W0^aJbSp-w%R%R_IvG7K;s{Tt{YO?(e*v$TDX+bPQs$gKK2; zaM8Uh_uMAJv#Q1|X*XCLZbzFDry}pDs?A1+HU1O+U02Oe^n9eqNVT}dJnwHZC0+;} z3U1N&CF$EMS#rD`F4t2lcgmEMDQOCt(=h*P#-fmBb?4#s5vd_-mC zJuRiStl^L1f5Tj zlChGfOSEO++=zvLMtkt=p!S;oeE0$5nZ@z79 zJ}@P?u6*8uuXpS&{L7pycUo7V8zde5{9=`!MY%+laN$zG)qBd)SMYJy~IFOearm09K?ZqG70@KgR zSCi}<6m|CdD>Siwwu8X0b}4akRc`d^u^ae2@+~^1Dl4jm!{7$xsrc%xRChhBP{o#6 zs#0(K9r6&ncU+9$*J(dw9g6CF9^xEUHs*WnZAd;5_d@ncTTfv1+4an*5cN-P4yCzlQ z8H&vzx$O6lP3lSUhj=UPmrCe9``R(Nq;$f)Mnzc81KZUV+sqzqWS}vr9V?Zz&A*HJ z$!N-nbSz-a;*-Vq!363B-v}=Ur<8ch@Oi+NTgARLW01Kh(lP1k2uo(tURQMV?g2xu zA8|(Ke>j(dm*Z!l$9J86K9LgYMb@Jm2IGuBwTw-dT|5yHdr&zo==@t(QMf)+uRM$x zaB=4p(gui4+tCdc?&9%=>X7|zIPlYQ1^1u1!&Yd_F~in!JuN_r6&Cbi@|Tsa zvvtPNW^~Mn)CHQf-@X$MxPgFl_v~dd{tq~EPxu~aty3zEt!KQc%O>jLELOoF_f9YF zV>FU-P<3a;T2w7dQpt^Lmf_*}rE8zyR)>A48-2$|l7C7|tamkVfQEJFhlA&Tj(Bh1 z+Uss|V<3nz0V)aImXZ=IAXkasu`d+^{Y@S<~!F7>XKN zZjn#qo`Cfn{t{@Af~Lf4j&gFEgV&+2%?FpBRp^3}?yejs`?qaoaFk>!GgCpYeZ^{c zCMZHKsglKty_4Tb&DWmv4GG77pbWSZ(fVix?1T^4TJ?UeD{O>KsIWS8ZV%XEDDzFj z6h#8kdp25h_dr;-zAka8veiEaQfD=aM;GxtZjD;XUGP6QPtw%{P?=N&|0}(8J_HtY z(Kfz^yheWSDgjD+j+{}G{pa+erN>M9cwPXkWK5!nvzLp!FZ=E_dk2M62-%k@5>5+qhe45fo`YC7%z>&o7a z2(#zNtf&H=r`00KS<(gB>DA(s#S-fFKD^!qI8_^dwiAQta&Nx=y=V^}|93ih)athn zrE|ty)j5U3yJ^%^DE)FZ@D#cTr>iGJHe^O+182~gY(Hy&zR#HJ0*+T;SHsi)16h)x zK5CJ>5r-)@<4W0RG0Pd%g`eA0_*~9uk@GdE_kFpevEw&KJDDV0o?`VE2!h#9_!gQn z{0wlq(I{Y#j)iK6iIsYhQ}9ax!`yx64IeK9Jz%7+2Y-aKz|WW+Pr}yC!4g*#Z_sHX zJRWv1B8^`tr~rk_99&3CD^~<;MH5I!@@}A!4#r+*9%fH*5bVgm@pOE~AiJEhvMAI; zemQL#!E$uCGO}c)&0tJsCzDz%xPk-dbrS@Z}TMwcOuA$RR;m_PK=B$f{c(ZSWEK&x-x@+OW# zjkUOP(dGX!_>NO<5!R})uHehi@EE-m`#f9vq-2oGeJFoIy7Mw%+$OYC%U#vB|D?)t z{301aI8!nqEqKA!4+_euc4hLhH~A$0W-myV zfom|g8XE|zNrfH?Rjw+eCAiN~o2(&)*|67FfL}dvtvoaF=>DEvk&n!M4qA?lNDtMM zZqD&wamRLYiDS~4kQozxIlW2F@mL+CWB&XL^zV5@(J3td^+}-C^^6U_2HQo;>0~$s z4Qs|WBI_Uc@YOAOHn=Woiz;l(`Tez~s;!EJqY0>7I7qUrVb-SsuWzh-D_ zg%oH{sqM6udJB`n9tZwXefJ%aavo>aQt&K)VvVTTkhx;K=~l$S9SXbgdj2I<3-Lsm zvPZ5y8n4pX-?Sv?{?&>G0q%Z5oTP>XaYu&3DXS^{lw;sV#J&j_cU0U&Z@g>CL*y z5^s2UFIJ3y$HKLwVK$Dd(7z$Q{QVszrs&0`3q8t*=K7y89v)lC9&RF1g5AyeM8*Rs zv`9xUYTBQUeZ`cb&B~#(d8?47anAQNV7ZzRPJ8-w#g54Bq_&rDwvg4FWu{@*OY5+j zXmyw157}!!-*XIS= z6?Cb=x0w}ki{fWCyTepc7+6t-;Du536O-McHubPRqp75<9g3cQ?mY=qNwL5On&hol z$uhnf@r7Ks?M3uc~&V`=>ur&R`Sd&7krs#=%gAwRoV)~K~hZS!~-V|~!L1}*;$44=Al!Nc=C zdxE^))}pQI?&KI*Y^7MkOs>L}H$iXH9!RyLp&A=%hViyL22Ge0<=Y*DjuYDYX8#W4(-OMljIqZ5#>t7se_V?6c_thi;Hn1OA5>t?uCZMX$fvZyKS_q?rr zAo};9?YLu*@~px{&Jg87*E*<4U6PpS;c@H@J(Oy=dTsL6eMbl|r6B zyzUfzgPf?uuK2w${JC+|tt~6ou1g+P>^GU$gHIZD4AU3kF2Kj7_j$C{xFhsD;uc1D z*<7!jc`Tytr-osJ>C#PlKi6Kd+h^vorqb9j0h7 zi(<@!9?mv!ia+{D5qByu&#HR+`~q{dU1Pz2iYH?g_+k5bmeUqKSJVR7v(M-zE#Z3n z_8bM4vv#S^dvd&3GK`hQP{^O`=M(tciPq%B*Dr(w2*L6GDfLiiGW0NH6Qn>Q>8l;mWWw!8Mg*(8LGyD(|iXHki89 z;GTOzS-`p+<{Z7`oaqm>T7b#is|}bNat_5lVZ9;h@Fs=wHRoWHcB^{S==0BfS$^87 zFKq3|QH54=no=Fr!$4nB zbdRh36RwQ+H7#tH6c+vpKYOe>(Zfyr3de&Zz- zAej*E7_uYP7$>)l-_mDDe?oT^dmWlKo+|9Xe9K9%59kr=>4dM><12bKsa>g-bw0rf z0E)dQ%OVejB@G_0eV;Zs%nEEwL8+}tD07nl{l&TxOrB1vv-ucCKT;F!pN-kX4b!ZN z;hoDpge>6$!lsqcAYU?*LpROkgjt3cg8gXSp7FtFjz_arbHBBB)zAE0x^y^SjJD z9L;|y*S zZ9~&dQNK_!2kn0cD$E4@v#_O~6?xuX!8>3Tha$^_3f93nKY2k`EEFlsRG7FMAFq?K ztHBK2$h(Dm$Vo~M=pf@akg06!6QsyInV3$Uc4IAeAG1aMHdr~dMhAL;rc8a%d;9}P zzAmsL&aZ>n3f7ex8EsQhUQUU6i6gb$w@qpFcn2)AyBMW>=T4A6cCH)cyFEd2_scA~ zxz(E{>T_cLk6|6G`f@^b6Mi{i+JiOr1)EJ?bDv6q4dSH~_IizTyRIs^_nkcDF4ADu z!#=RhzX#V8gW=Qh)U7O2c$|R+rMO&~0@YO|6L^a6?dS5+P?yq9hB-&`Xp7KE`$3M| zK#lIcS(hP4*9p#6=Wv^7{!l~f`6L^!WBP4FUrgUJfWA=AbJk?i-)vYJq^>X)#bfhPq) zQy4Yj9fxvZV>);i!@pWj8Qd|m9==k{IP6@;nG!PUbha`u4s!`3uVTg7bu#%YMQo<80d5%OwZ<4k^=GB5y^O=vLVEW?pBuMeC@ zInYGw?3zH8Vj?yotW|JdV?@+P?j2UqnH*gCWZs`Pgu=LkE z;z{d_RlU1&4bT=bk@8XwuJIVK64}Iq*c0sESI_w8Lbvx*91KU=y_a za4FO2(=sS0{ah}&W>zg8x9Fqvlh71eCIf+thlo4rx^|6g-kEG=mF| zm2lbz$tS{5&$Hk%oFt+O&yCH07a9_|(hS?G`ov*-(Hd)MsohwXGPN4(;3(M7NUJ#T zzDKl-vgf%&^H|WANqM-eFE_B7KbP@sP)!4?^~w=cRna}4HtCbwD^w1In3QL$|DUdx z)co_;ZZ~MYI!eIYoJnLt5B@&%_sg~AWc{0eerL(Lhxv$;TEzezCe_XF|cIzwuX59;bf$jEim~G=^GnB)Rg7n}Hd?hoe%aA&$ z{PCz(MqNver8_$4l^gQ-O7e-4RlM7QqB&6_tXSkJ`8tN#P*A8rlkO_H-NuIoKcA>(2)xZv9g6Iu;F~2t zs_oju(0@WyZBdmxkEOqwL7t5rcE2>MO3BGzW!ivUSc_T=gFh`1&>ymj$Ulmx`}3@w zXNi~}o7J@iVl4qT5gqbt&*-a}cct6MVSu#0NgGxll38R}gp^{^Se71KD*fVf1m0o| zPbfD&CD_DyIZ3)aj5FE$y?oCQJ-9i)ie39y7|_hzzKV!)xs=8%dt3(?|`Y;eLMXvaI%!%W$zx}ta*bWj=d-oi8I zi}56K`TmNydl_NN z1Ab*VGuFTvB%Q6(ic7H}$DjDt;wA1r5^$ronQ`G&w|uqxq?KLTB3%lBrkCIkd6n$= zEP0?KDy=)OKZN)3n{uc@H$doCfqj+8ncDL;+)t|P$JIyZj&&?aG@FSBjdi<5M-WtC zt&;nv9a+q}d!I%H*cgF{C3ek8P~=_o$WW1j25pg$Fk=JV$gZ*FA8TO2#hEq}T6tX_ zdy@dUxv&8@zn&^8BU<@631si3*Myc*O>F$c<(Q598TT<4)?>pZT3S!`q!+$C|2t^a zrnKco7dEEHU+?b|wU1e6TK9&GEmv1`t35XRXB0K$*a$3QU-PiG zTVVPCw=ed_(e#1?K$%kAa8&s zX3Y1B$Y?aB%H_9?4W~{Lv1;)4iDKr88A9*&v%lIfOv88udUWZOVJ`Rsk#*?8k z^8>7Z&cm@o2y9(S z-(8){54qCPB#behw}^-z4!xoOjPMCqJN^j#KfU|^4$#+0*QaQ*g0r%|2Vc3HEPLKU z`n^ziKLVA$?F%#&oYE<8sHbF$2)lO?lRci~RD;K$GsBX}^ORR&-mN;8PxjnAn8hPG z&Z`Y9A8diN4(2e%=~)~~R`=XzUEfSTMm6~A-B(|Fm=g!H7Cza2$0%5)HEj;9;_Z@^ zlAuAiHPt@jzctFnH^Yj-0iSQp!pzOZAKWIi3H~A4H2AyR`dMYSbri{W?Gn?W(=Iga z`01?2ttHKA_?bagQ#TmVk#h%c&_>yWt4h;-58S?sN-$$XAD5RnBI72YIhwm++s>|c z!xTF5UF4dOY&>7L(+Fv@l3TuHW@4%vAD?U&cF``hG&JIB_h>&H(8`=Ms=jTT;hHV} zYaai(1(dP+r#>w&3i;K~uSMDechRgU_y5r4bwRf@7HJ~HPS~m< zjEmxKrN*Mtym-__8t&Jx&tYQvKM1YS9t*2;Qs%A%JjlhU6s)w=VAHj=i+dO=Y9dQRY+jxm@;kV?{99-wb?!}e)T5lAK<8VG877G|D zK%NfkHw?@O426TM1T=yiq|mH=rv0p1gr0SVP0pFP%S|H23@1N`y3>o{B-jy@BxT{Q{41wD#@4z<$v)|Aej%#Hn`~( zo!1cr#xx8lc?LJVYc%C*!yM*S%O6iEQjA1J8U-7U(AKY)s#Aq*%Rm$V-ALq>kxE_? z`A|%EKx)%NC=nNBV~3jLY$tHadG}Mw_d!b;@XmTcyF9o(OiINagfjXE{7;LO4dSdB z>|5Kto{?U9N>zE5@S0L7^S*HTg|1%`q){3DNNq=|1XI;?g8D^#cB_E zw3mx2k6@TYyHZU;~{C!IX%j8 zYeMAYgz-CX2V zf#ShFf;J%G;?XL0jbew~F#+VrX+qG%II#oj4AHs$O?ZQ6I^I*i_6P?I>JZ6s*#jtvc{L238LIUGC#oku9K7Uu;JQQ`f6+qIC z$sghOpf5hE%JXn8dhTWZgols%FBmXJkgpo|C8rwIc3KsMN@_LG{X7i03TZCRF-SR? z)Js68ovZrL7esGl4N^kl(|QnvHo<-c7KtT5tsyLNS*4BPg)aU@mDdI|BIDMPMgqp+ z(uM38(`(TrIXGl%{NAq|&9?r0qkUc@o*m&;%L#`hFlA-pZN#xy40f9ux*nLm^ zA+K8eYtJ46b3e)?i{6l2S+h{iP(HhdB40M*mZ(}0w#f)JlzfI4U9IW!+epW*2@P``^I@ADc!IzTB8;8;Ec$H+A5;-$DcG?+q9 zAo3tvudRS%q|u)r7bhf~3Vz|bqG*^pOJ06!U>&0MR0C{X#sy>2Tp=y!?lszA8_8WW z3Ec|sIGNHZ2l{Jju_)AWxd-p{PKU2C?{#N+v?by08C%YbMG?`-~4?liXbu}s4}TN zHn)%OR!Zt!o;rt!0nc3zp%6V+6x#mzwecX=W{_Wl4}C08wp4}vdRMnxWkNwyR_H}` zsS}yQCH@TGUsvDEFA$s5d0D7&e%v@XV}RcF-CRb}t5b8!(cPWbR7-+=T5l%D(7r!K zRyS6FbgW6~K9BFyoHZZ2!*5~j_+;um8lM459F0e`h8^eo%W3?m^)2<0WUnD^5U`Q&{v`e3WF%?MhN=YFmhEr!J5STZeQ!tk9AiwhIuJ{2Ow5f_8>LEc2G`5M`sIW!r;aiLHmynWo z8F}^lmjijg^QzJg%6$f3J?+?+Y4>uw(^Q5mzj%bwUpv8E@Cd098nF(G%KJv>CW9gv zYR*BMs~;DGfB9*+={)IRl~;_NG*>{4h>q;!U2{rKh7WF8kyf?lV@Fxdi1o{cge+l= zO|TBJ_tZZ-7Y(1>`<2X|8Y`;aqBF&IXHmq*IDz-ffWr49hj~@Rs5p74fzhvsqLzP& z(S=1jK4~-crkoj?86vP+e0q0~O~%6CuLT|{jk~TZ^orD$;ha^J;h0&9|9^ZDy%kVb z^5t{$qn@d>h@~=mDX@{59~Y8#7mwWsZ)CJ*M#FbS-z+Fmm{0DB`x?->)W!owFbsSa z?d6lF`RAdi;O^3`y4zrK)Y=^%H~*dV>QCgN{fZXTC)8(N4soTQ%(^_ByJ*TOL8r%PYTH$> zw+@7r?}HsvIqb=Njdf&3RPyt$0b6#H%ZfZLA~0vP9H$>WUJ~{e4HQ;MPHss-q_&pz zD6x6{c;-QkK(e9Ux@b7`m}iZJEviJ$VJxM$obB!tZ%|a;{hHhl8(+sMm41o96I|Br zI*9T*S_VuSNBa%0Yy+txGt%r>>iM_9^7r6`3P0Vg8l#S1}g#Pe?6BR7iyi?o&`_ z#JS7w#te`oi)dZcU}}W_Itz_1JVf?NYV$k5K9-y&)lywi52K2#29>ga24`Od5d38zYB6sxcAO7NTt#U^Q~$T zIcr(>Ken{#s#L!L2Ihr6fWcNc$on&y2nvY#)WQ#0Ds|uKnHnpm!gZLpN}Wg3L!nY| z52V3_r{LZ9qmZplf#~Gk#C^WhQW?`m8kAwGg#>rLj4s62`2LH|?~MDO8Q`wvcpTLs z-ZR>ON#D0_IJq3jM?GjhUR1o>h_)8pPb16h1fET-8o1#FcLCP|rXNOU;2v@{oL*iX zPe~+8;5oVXy#{ne$Zz#OjzlW?b>o4e zZ8~4aKd55e(xK?7l=KK^NgczEQf_1{=o|)9>QAdukw|$fWTObwAUVHNX&VRfe*bSE zo-4(KOfBmk@=5KT{$Z{**a&}w7KMsw8ust0(!aX2Vww0A`0gk}=gEEJxpMr_3u3ehIbE)KC1KsaIS*!%`@ zl=cZ=OpG@o4o}x?Xm@`Q}t60)R`^q{7o;KH~QGzc?u3{W#FF}$Z>m=(0({e%{j4b zKzpm(*1z1JXx!f7yHZErtWxy%&KY+y4u-#fr7v96eA=pf0sY2w!>SjRhi|*Ot!$gK zaP!3Z2RDE6tg+v-D*M%+5c?1uwT}PtlZ*wk8Dn1A$9`_)2aYBBjkb$%Dogrh)Q0*Y zTZ{}*ov9Q|gO!Q^9(urzO)IYY=VUsF$kal8`s-J7#op^TdaC>efiXH+I@jm}n6R&9 zA@nc9^9_Bq(7BByg-1zAP#pyeFOkwG;|bu~ZwkXZIOT83h}niBu6un&wP;+7J?b#@ zY%OP9_@j<$8(qP1naLb4GB&LfHEOBBF>7TFVY@fSE1Y_2gvtkiCN5B4^QgrreF=L0 zL}Cp8eQI;@geyfLf0Xy*oEI6TdMMz&9AfUhu%=et=Yn{Nx}Ykt1^wX?csE}9;|v)S zUrc#UZeFu~)fIM@CwH8IH6aV(28Fu$*<)l$@l+Mi0$GN3@&AI|QD}Q%6;Xh}%DgdJ zamw%ODy_BwuycE{faRJuE99J{4Bca^gpk`#;7Y!)f0^Sb2+FNSy)R@fk)HKQVH^(d zI!ul)cb>QWK-qr!UoOBOb;5`223PYjhB?qR<+iKD4J3C@Qz^Ft3hS=mofn*}` zmN6Dhi_G&xMTlN(XLOX4lb_$9@&89M8sL;;%F@f6o* zAZ2%FO6>-yKC~RuV2GMaaW-jeqP!Y~vh;-}zN0E^D0Wkp`u!7*Ys7ktLJSfu z;I3su{^-f*8C}iI1TSP7k4i@R{|>rz(j2|N9*#*lZz$r~2LnW`V^J~oWpTLs==8se z#0zb(RGR|QT?Z|<(nmF?TUmKd;T8*#C43P(#+bc0r2U#q&D{S?6lMi`h}6VCYOLH2 z=B@RJ9(4I|%^+FgZYfVHa3>y}Rxx*XvYJBgj~Z=gsDkuBhf4+Y@ps9!TAoQC+=(iy z$`FH0Id&nJ^p*yU(RMfKf-IkU6ewk@h9$z7luo<)edI!q2yY*swF2*sQ*b6qDMM?< zHL_YwLPIa8=$~@u4_DU@|3DX3d$UhiSz~o z`1{63P%E-rg&gJBZavU+@E&4z8k(KI)3((a1M8)p+SKQ;qi6EDp}F~;z!k|yvRJ{n z9mZY*k|@0HWEhL4pGY+Dr8tw8)T@blPOwlH!?ZC7Lk_G&(mA;>gNxf7kr<#m>T|>Q z-D}<;Rnv+EQJj00E(eIhBhX%uu}kp`^|!fUr*(iQ&NNw?w@{@XaRg}!KOF~?L= zdQ0WykK3cZ^?l`wu-0&m*Bs*3uQX)fHNpP|K8RX#Te%@bkAJIo;P*Xyshx;2wvA7V z(R~*2&PPfmqz`x0Zywy+T4>cN3vV;~5cJ*KV&uy_*E1A0qI0tLBx+*EB*~RQRy4OY@{P7}X&U&l!`umCMxC!LJ z@`5!m|3&48E8e8vskyXG4V{!{;47EC_~7Sgyh&uC-eiAawP|Bn+Nhbsi;SYA(TPDz zC;E}xT*uSvRs~&g3~KJ`CZ-A{e-~-akS7x7LRu<|iO^r)u?%1i&5D<?Ws%qd~YWy9Azv^^iWRJg&AEL)1-vnp)bp9)!@zHwtA>dB>Y}5?SG<)aWt0i*Tr(>ri zCuolwkgbweL_g6RMx+WQ113Qr|98i~wBN;fIH7o~-FjwA8)5sH-=1VUcb`Vw!LXUN z*XR+rCduCV5IrPm37veGp_o!Op{J5e9On^Mo)B~(%^u`y_hozpBOMv7!;D}Px9l)B zSi$H|x`&NxtIb{dND~l0{bE6j#pfO?(%~tH}Lz0-tgFpPCH^FPXWwpZFDp`^U_;JGi|yhsT27dcz6fF`sG> zg^fezw2JTAX7fk)qdm;^ODtjRMN!;0Ix-3uP1vr(n?)kiQnpwUJg#}C1R96Tbrf*Z zO?|aS8M>ns5&V&mvuE$32Z$Oy-$EDu_vFzM>JMc(pa zJ_IXsjpw1G&5g?|DPlyJ3XjJ8pq;P}R0&Zyi|iq_$WGueH%JMdpbbQCRyAIa1hjHm zPwCkp@{7#dWk&US7?d>(st6V-Bt6O*|D9~6FFQeABo|75@#Ih5m7EKwx7N4L-bY^Z zc_e0Ppebz91F8WtwEm4KVfFoxSF_s0YklxoTJW89cu8F?X)cpoS6$e9piJQ%1=Pe69aJu7DJ}kl@R% z)QNYqjJQ}A@Ka@S@B#6yXCM!9N$RM){^|E-V|sAHbi%>jnc92o!}4^f5~nMr|f`qAhy%EL^}BG2~N19S9x=B#Hj$!{eu9zCD0 zk6ocM;q-R;@XkOs`1Jt1jiXHX+>K~n>DG@|y@Gb$zpJ~I9ft^-PbcW&SE2P7_xjvB z)Ekh)PeODYZqf#$O*Fb5&_#r@q*1|^b5!t&JfPb(9i8W~1`qTdF&aVss&L(Gxc9~n zbUsFkUdfIATj=FJdD?$bQQ1g62zwPw!m#4lOEykm-e6J*qX)nI;RBW?RzFQI11&(H z)SS$!qI#EXUajGwB^8#+=OD=~0g0e(8R|lvwM4QGOy?$1pKt=-P9X zwx`-hh4rvbQzQ5+fl0}2QI=F``)A5a6=AO>Olv~EDEx{MRcBDfBsGUDxBt6lbdp4u zM^hhQaIjmizSDt#)3I*ejZYYPE8x7U&+vvsDI@PB=A*z0kY+dV2eZK5x)iq!fi(0% z%x;}t*G90#%qUgECFt`k;(@W67EK@&g0T5OiXUs{*`YRP^w$wmQz&^P$FG{fQ# zz&11{WAr(Xp$*XS&pA-}sPod@^iO%ws5zDP>B5i)s$Z4TgN9KEsY5^UL9AYa{wjr~ z)FPKXNqeJ^(s4P_GvWFνDgWA^A-w#^*_k_ylse)u*PMS_RObc0M?BkE<-lMTY{ zfvW7eGOnkP^{Iz+XM$6#bubRGEwGQq3HKNzk5nf$P}{irvGnvXDYzzqYE@Aew9pAX z(|^+l{Q*8mZVsY0W>D!)*p+TXZXgg{dxGpF59t>gwc^IvVNzVg|CCyeE3MG>S16b8 zf;wG?$xcV{xM|x6rrl@NVD%AoE|y@*>4AK|K+Px} zcJ8sv;;Ot`c|_oDQd7;b0L2q_<>6#oO!MfV30cY_-M{UU;aM%C*!c9_-Wc+Ij(9|J zQSi186zu}^m*MN-yIc%my`W)C=24)qEBp26!j6r0f|?%gOQQ$N_r9Ne)(clb7>%z- zX+bYta^0GmId(D`J#W7rapb|RG*t&z&HBiAowT_}(!eKreiLuiKAiAb?7Dw$Zr$mQ zK;+fR3_TLt8xB??d!t}G%G7JZtL;d%K)(NNRwbxnClta%&(mux=BgYnhB2tOUZzT) zDprUQzv7>y{zhY)wOwu@Fzic*b6b8@m7GSjGV-0UCgZiM8Ysx#V(QYl%xQj7*0X9f z(@!^Km)^_t|1gD?Ulbbu>e%xgmD$<4w<$brz`LLBWYlt7$M!3u_De<$&z3P;MB>Jf z+QDH=INf6H%j-Iw_3$=Pa1UMB^}c-!n-i-uevLPg5kx)Eq2tw+S8Vec4SK0h@IAhb1MVWhLshwU(3LW#`D{|0w`iBZ~PmslfTO%N#%Wa5# z`!{h*RjW}dptc1O$kdZmhhycmJi{;tn3)Z2XFJ8BX;{kPr!RGYGbvZ3WU&dZQd5wk zdY!;|ElyNbMTOME%~S0z|C=rqY#3(@IXpU+dAyruh}hDA@93D0<`}`(1Cvh1`Tm}2 zBjF6@-Uo$uz>rK~U;CSe;@Ax)|&% zc)#5zgD>5qW5r{K-K;02M))l`^k!+t+wyTwL62bjYYUII@o!v${`)&N`Cji>6yu?F(iNhj zv-DYq@%Esjx0v<=SZRvyk1|FNZJKU=CD^CM6b62=P;h%+u8yCcQqFoGdDYKccOyJM z;Mhm(x5=buic7UDq(+VtD3PTR`THz=EbY4E*bTCt5$D)j;IAC5f1hdEhiC&a$^6ua z-UTs;la`$(E$j0(O{HBt@Onr(!D230IsIGSAFZhec{eE|$Itg;3;mU}OV-C^^n@Kz zW_x$?i28zJ$7i=7)7^+0CN<|)BVC}ru;|S|86)GG&Wy$AD{P5UHP&Y#T^TLZ(|Og% zU^@1hrH37{);M?R!1wtA$Mf@$k^(^O9nprl)>4iozOQP~6pm4{ihy~nDLtZXUA1CB z2c10NW&%gB)bx;&Fu5qbt}^<{m(P74c?NO^Mf{uI&@K1k z#ZBZ;6t0WvX1b2ayjzSsTQf3o!eo4K(xoyWbhSB0#%ghptFxB(_gxnsJ0O-y-7_me zD0z&C`=91wLe_B}zi^RX_l47^qzF&)OT94qiazQF?!S!*dmX zo-#EojhL!Qx zRqLD3cg>~R?jB|Ax==qgLQL}1jq)1hOv$Lxg`HRZ^i$VDE41*>v%k*vhrrh%WCKBS zB?J8~De+IW@1Rj=j*PF8GJTAt3|Z-VST1Myn*V)zufwlj964jrr;+DUM+G$)CYk;t zA5xmhCWZws3J6r?>VXSmA8#{w)SX8Ac7=2|<`|<4-$n(kajYAUgA(n>QpcD}-q8n$ zx~J4K;NC-{dw@BOzhQ%AsASjeXOain&*0j1}e~GQO0my%6^SBUh9{ zz5NFgbxjVa5YgJK(1L4EF&EU##Yax+CRiipykeGuqwlu5r4~D< z3jePh{2)xa&7skpwMdV2%y|<;fN~Zum!qN&m?|3O)S~Bi4wV;KX_{FuIFy16TFqeO zu_zpeev#nrBP&jwTv-xQ^O4676f-Xc^2OLr`!h-(SCja5G&C>P$fl;8pcmYv63Gct z*4i>>KJOTK6E(<@b8N%CAIS8w%L4W}jOQwPM&x6`<6pp~&oGOFZSWp65m$#ud(^%VkKFn4MLKqyu)TEbg!`$|ipm@b?HcI< z+-jnM1e|0qeaU*x2`JpgV0Klmo!8=m-bzyj*sYHiU1|rBGkRO2u$Qr_koKzhPtM@A ziSK~Yxtz6$%t*{}a1on~-C=ig1fW9?)T7;0i|9wnV@*Rny8BfvnTSgUSU`i46knq7 z7+JsbTJ2ztir!Yj7)nKz(h1d=)i|>ZBc7p~ZzNfNYdiUdQ4zJe!BTS1s@R{Q3rnuJ zXK#+GM@HxlLz;`jnDlu;+a_7PKMQPt8d4K-H^9T!y~%x9OhS)jD`@U6NHjihG9$#l zO@?hd%ZvR8^U7)RweW;vntw%P!vA9dV9wm85|}jZ0v<=$%NS?dL=@PUZut=>aSzapA|HtJ}2nNlEOd3 zOzC|hImiS}b|rPG-qDXApC*nbb2UF zjvJhNG6CKENCVR2Bi|EA#wvKQqR3wk?OYCQr_UFo3ABfu3^22;q_~52#}O{4!{py? z(5f8se{^LfJov^2q_!3i*a%o>S&Yhfk(g+L`{AJ?b9msyVzJp+dOujK%{BTBex(5o zPDTDXq+YSDO`r$v-+NG1p)zmZv>{Ox%tn5N*?3jP4zeL}w0`wO{VrSsRb^Z~mlbAm zn=lNpLN)XF1)Pth;R49-LZuoxe{4`;=HI1N*p^L|C>HR>y#=={h29G!Lou1 z54kBkj20tr4!Va_QV$k#GB|$9kcTCsL09m?UIhBpp*-n&=miC9lyx8JN32E9$@=sl z7s9vq2#?{995&tAScyKmd4cU#tImCK^_)vRYJxlL?3`>&JBVIV%4=$+CEP&^JFn zTDvuuwIPg{@0TNBU8z_GJMEVoel*q?5?>esL+0H&J#*Y^h%RIteKA;$6^{-&9;U*m zQH~Eh&jcwp3~C+C@@IH)ETyznpt1qZOj(^WFh9?$?CATu~y)`Hb zYGfzBduM%NNE0;u#?dlb8Bl<%l|h3d(ED3p1bZT*Hy*jcnv0P}`9H(tH5EB`SUe3N zs1IoeTBZuSYbUrDl>GgPT%-|LH}lT)cSB|=&QkMH`|3d_(qM?;|9@!vuBav!fK5AI z6;ToCL`6kFKtOtlUKCV>NCy!jAWfQp^fD?}5h)Sr5QvI^)JX4%)X*cnO6UN)v z@vBj>Qn!m8=xWxXe2^cM|3YlWYmEzG)i$|c^b1btF|vF}M&p?F>3n9#vpgp3to9rvfTKo& zo$x#3W2o0MP_qSVS*iB1^ri|GLjrT0c~=*D%pXh0m#uw2u)(HXVBJ3eg2jf^=+^>06lMEz(B-&hp9#3-zn3gBI0- z2~t;wjAjcTFKf(WuOzegL70pvq$})pukRX28v)|OFGF`4gCUD@ElBu-r#M)luWPYC z9Cbcq2f$MecfOTD9qr-zM#_DeIc7LP7qo(XXnH^9%HHHu;~UZF%`XRXS5*^&!I&S~ zJD2~oNqH!LzS{I|D^%F9uYW~|D(bRz>*`y%bCuz*&2MLHoVsz7T4~8TnKcJAUI@p< zMs2aqFaHYo_iuI7(0lfbD^SUoN6i2Zm&{}Lkz@=ig{7hQpC2n5A~(g#gQ~aDs$l=` zds{KUS)Z_Z(nn(ILUSzs{^RH=+iQ+Bg{2?8Ux)%~^D5sriA|m{_|T9;irjvEH-^$u zpuoM+8)fp>`jB=74z{k3=b_Wcv=dlYV}qBPwgluuS@+Fh1(NMC!!PKI+@a*7V7llz zQTIKn?@E^s2QVDSC?uF_{lq*_+3gt&kLZ499DnTLbSxBp-NYtAj`G9lDM9#a{SI6U&h{Kj}xm6k{HLgsDL936b*iW^&=aUm?2nA z=y^X{_!Hfr-)M1=X|aNCxL#?R1v`PtsEUn+qJbkn z&uq>98Xek!UwUk!wKs<_)P!56gK!F;Ai9LAvV<9_S`~cjk1upis~jVmr_>Z3|Dsb3=rEc=0uDDu)HH0`)Ral(n$19LXX@W3#gl zDut0H=PV%tDuf1{pR<_ztsVHgb*#3WLoGXGZOb0vU0t}0Yg~N>UPUoUUW0L`Z1>0j z+JKWKo(FkwuL^YLa=))GA}gLfza2yOWpNFSodZzN8?VQX<$WLUQ5Z%wxnEFl5Nxj| zX-_doy-+t)BM*`oAtm@;MP`r+C@dE^^{eE}7#-+=OV;I5uxe08&9n2RO{8#^Zye4$ z74m106L%4blZ!})Au7449(=({gJ2KAMi8O?kXczM_&!&PM5dR=0a;x^whyvuUG>01 zwM)gitjsD9-Tpp@s<(Pwg~Ag#3JhI}-72dG4?9(Z&+O*C=qWeB4XRxKC2tkzl-CxZ z3SGx>5FLLsG(V3$C#`;(jCp?K=%&{OGlDk>df{GOsZ!p+DYH!jKFe{}%tUA@VW`fH zzSm7hp=6=VSWZ6L3)BGDogwiJPb{!jpBq2kvIbT=|8jTIeHML}du#zLw|mT9AS!Z!2< z(gU$neEPLp$8kl#^|-*XC8&i$YR6+(lN$q_ff~x7_{nB6b8wdB#RLK%2Y^+)yD?8e zBGnYIjPmqH76{W%f;{*ZpHt`R$dTscm z$FN*w=y(r+j(+F@g%{1Eu&&5IFl(yGu;x=j+8lbGs1qxl`V>2DF;I!-D??BWKp= z8(h{lfDMC?yT5>3Y{Ih0BI}8}76nO}G&n7n-3s({v5E844YuBXX#qMI3Y&u!agYi; zO`{dw06hacv5}7K!@x4c@s4qw^M&qly;j*qMaDn+^90hk1pYu>xqOQIN6J2`Vj=^$ z3jR1eEe4GxI^`9?3D=~0SkA^J4EO~ior{CvpU?fM1C?d}sOH7ZVD~V`>xRLzQ2xW{ zap zL2TeyJr9qp$mdW;(hW;ZIXe4F{kc@AopaV&sC1U2F`UFWbqq&)a!~^gfp%a{kY+<0 zToOc~@H6{fy@2Jx=>|x(=%WpV?QAAS(*QGrp;)@*Hlm`1KZA~CShdpFs zET^jaBnJ(aT(ZsZJKYXrl0dr3W|Tbs)!nQmR!hf8?JJHf0#Qw`ckIIU-@-g5iAIxH z47+WX~Xa2c;_je74 z+L$I2iyjp(05K}V-OCkly7hj(G!!Md$P;>41_KwPe~f*;awd+`2s2OJ+;(mnHQZc; zm%pdOXOn8;jz63O+F}jX7m!ViIiZ77E?@uyUIbJs6aL{Qe@C9wcfsP*A1*mI~ z11kY*hpk}(ZF$eOfL*%90Z8d}ug$!IFu96N6r!+rTUMBn3FlkG+lDTY+vzQ7U2qfp z{X3(scp3MOAZQli!|*DC9%jQ`;XGS2CB1(NYR$|sK1m0mcYr(VAeIH+c`Ud0rbA?` z>g((%#Dgb@P)0R-nuTFT(nsx~ez)*FHe=C4!)%G5ApOtrTdHzjUxmNtlbCdkP%!8} zUh-VdXYq@PQgih9_2$3l(u4^2^7xU*tgmf#q+go9JAP;X$)@vfY0|>o!5QQMlA#Q^ z$c?)zZ}8?v0##{A;h#Se6UA;jBL93_?@)_i^Q#4AUB(D`hSK8;mhk@@VJ5Ib@a=fD zGm7t>9%(qT^6!MHaA-|Tg!|{Bc|nKM;$si!f{?&wp8{{quhDC@fh+XNI6UR5#5%6kCd{Jqk7sPccVi@|k> zhn_-6haQ2$CgGWBAA-}QOR6^F;YdLp)&hHvSDpZQ&Ow1M=8xf!?s?jU#`QQBl+T^wJXh zYcz}|CXSp1PX5>yA+o7VDpGZ z&NlE1o|JK@ifXY<{FrG%#uq|7tHANiy?N|rulNLpu6TMF;Th9y2MIywbaqR0tQr&t zMUn5DUbwBX-w6~#v&U-lI+O-SV_;|{3V=OcEL6B-kElw(L6u}wpfR2#lZkJ zk4RM=2m{G=^-Hh|ukSn!WEVp=KNVjL?K(CbNI&7Vgw4~ViE;t_mlcvks z8t(6H5#O-+poNSUD$@3gGXXy3%;~=BXdmx{)7L~=Ik~uT8OWN|3A6)$)=72Xp<`q; zP!=FUs!{CN5#89|CP_W~_T|YEtObgNli~Gr=vn%xat`J?~ki*{^c0_iYV{}Ao~1Dk<%ncKSv(~hg;0=<0u#S6z5`hT{k_3uBlzY0 znGGy_ZJxZ%Q-R%=<3X_QI?Y*M>opOFs9AKlB{W_PrLZNam|cmQ(@+#JJ~fW#hY~s0 zz{4Nfev%wi(EiYHER|FEl@xoEPcS!VOh`a%M&(7cX}svholbg0U(+a$X&T$B5sF|rFuZu7yhYCBAQT74 zsHuU!o!5YQaL|sGHh6j+I0CiOHPk>0j)|8IR!4InpCE9qEty{5#oUhp7$e{&F@&`- zbv@zfzJ4r_uh5P+I)7@PlnzBarAKaKdw_L4NGIX$j1aV<%mg?@I`$}rI@*xm%Mq-j z7#9Bn?M|(n1v=qFg{zPS0&fT!>K@t!H-%3%>R0q+wAdMz)6XqhQc@Urjed11F z@h-t_2Uz`=*0f9P!M9u3KT>UJ1!L>>6vsB4df5-9I^fB~ey*pyx}$pznsuYglk*77`WU{m;j zI5^t8Q7e>q$$4&j3^}(x#+GoF5eB1NYd^^jXo+sx z6Aj%R?}b;mj(LH9w%{xv-ke-1UFSl)fMLk7EWod)4vkQOnGDG`3wkSu&t#DApj=Mi zj=Yt=Zv(87pnNdkkoNXJ8M(EzT8#Gkv^Ps@zsXrZ1{8mtBH*e$PQZW`D=WYnxpTPH zYT*>i6~0xe5!?X>oE~e1vo0NNrEzgt~X+}s?llz_buJpfJ4e9xm}i{$Z(VZlQ5w!$|qy+6U!&H4=d zLP3L`Ogz1MK(!oaafLhd$+hJ3GOzs3e&^>16*2{omGOi%evU*Xwy%V}CU*LP#ired z%Sz!c$Ib5E7pD1wJ4C!I)Pdi^X5Nwwr703D2E#%rnM?-$4I=6H(|OVoqwxM<%2+Q& z45ymaU08>1hQ6{Rpww8f5NaCR#W!oZf{Q8tsDkIAAroBi`}>TkWyj7Kj(>w^GvWE} z{I5}{G>PLt^LVlnZ~$`d{Z|DUN+# z*GoHMY4~et(DT}X1TKYmGg|EWb!FAhqCrEoM|hm`BfoVoMz3P<2mjE+GyUE)F)MCA z+l-g*cAu|Jz0EEs|JDlKzU5!jnlsc{J@f;3xhRk&n_QfdL?DeTt^|9lNvMoTkh|h8 zjcwGcmRcjrI(6y{SS6=Ib*xxlJzx4>4NhG@N$~G#>>2%n{`$0JVQ#h7_it$F&Q1TF z2Q~hLu-iYJi+*1z&5d>bZSDDOLv6mOW}sxYF)`XreEuQ4GrBbQ(cT|-;r&Owvm^=s z_^3?dFJG1e_OJpv9UmH7W5Qz$L+>3Hea)Ps#tn@0+fEAbx2KjhfAr6+EK)5u2)&n| zsnL4a%bhtmz`vXMln2&@m{ZsOE@Rs1Wk0wy_u?S5v(_)TdQ8V^3-jh06-oOJxZZ@a zx#Cg+P!&zM#x|bOxi2PSa{Nd}`@nBL-ohzKo|?JIi~t%h;FFE|{Xo_1128TfG&G82 z{)4lJDAZ

hf;zxzMT;Q60ZWCqB`?+zh)TqUP}N6Fr`Id-GrGpoF^1?^T|+ zj-Sv=X@%7hAfL;493e#powCNq~LdAv5G*$*pr50#1fBE66p2eNB_6WP9!Y%Nx*y zk67rg649k*?Q=J;(}Vs@0yJnoIFMY2i-vDu2z)8ZBq~-tN|gD1qQhbL zpD-7^!nNq_v6&LjN2E(62c1lQ5BDwsnzco2c8a`^`_8Djkfpg=4Bg^_^Jhei^s2$> zQm-BnyD$gd3N^!J6F=g67M&#WHqJBIQjh-!WtF_L{(xA2uZaHgopDrx{+4t=jH`1; zx9w+PlesjY^gp=Y-!#$AVd>?Uu#~FYQ)2MvFNp~}Rk9hv< z@#`DC{B&JJM@s_sqW$};moGBYIKu!6grQI)W7BTBdSv4Shab^!{Llo{7O)gUWiEIW zJ3PAh$)V845OjSUCfDnLueECIah&aV`ge+3BeUf$f_;D^x6|tRP=3>v*Y$E=^RJxV z=jG{tBh>=kK44b*MLXS>F>rK_#JJ3ozLWVY+o%nNop5%LdMq4cIm8>1gWU{nmo87l>6A*3OK)F!nR5N1lAR z(Ce4MQ~L}yLgO;O2qDuqM#q)gyw@QbJ7n!TIKh==qj)Kk0bTx!!wrU^eirhCa6f(I%=OYA*XE4=>mLKO z7w1$_1@u8#Hjk&Ikf@@zBco;Gr_p1Ef#LJu(RM|aq&Qoc{o@?zy77(uz;Wih^^fNQJzXwR8{QYc0KUThDrdh<)oq4kILoumB=jndD zhfkvhQ>T~z(_x%%PMxy+X%}@SQu{eCD1K0ICF)P7UpDm5FSY%*4#sV8bx!OdsW3XF zHO`*^ryYK=Nz(L*4^^)$?IA4oG2anAp>Zjs;=W5&4+uP~TbPs$$*Ln?OY7qWT zkF39l;dEp*o)CVliu)ke64_oYZ}0$Y9Wt<$c#r(AK3Aa51%@p0ZRwP&y8CZsKB&4D z&}Rp=AX#vG{Dschrl~~jJliudp^ZOes44^|{({dUo{1T4Uy9jMbM9C2$j_pvReHxo z^sGcKoa^NY-z`i`6aIvuVB<|jgB1KH9iyM_eu$XqQhxbweNT4LXZrN6(F@_b$;Tf8 zqT6k6Hfk$Mg+?n^e7ff9vSm?2yGn z+cw@8nxisiqI&;f!^CdH#02{m)ue)YJy-noN9nR}f0aK7#Dao21@L0J!JoAj6Vwhr z<9k86J|Dgs;_?J@;f{tZ90% z%`=uB-_N2n`R4SBV!!)|W!T@g89TPL;ggs2mQ4nT)hcCG&#XpvWB9r@Z#M4;+W#!v zLNUG=j%prA>nhs_4j)@>`1eicLFE~MogdLaC5?S8w&c@4@Kt~0W@DB&`j%LJLTFFO z!bQ&|xEkX2I4tR(hs=iy#_f2Q9QMoPk{cFOe3-BmGnrZ0cl*XbfZ}_{zUM6Qq%XS& z@2dF1e0}@d`!cggpOU&Kl6-}-C9U6A;8%4p_LCnfJBZq0MwypuRu=qI)TIYff1i)j zzvPj7V1M!AR!`>yM|%n}oBusOht5505tfi}>mY7XaqBI-R|?;$Q!@HXk>D71Irvfv zytFmNW&9y&?mlKT@XZNH$xw$L)t7z3))kXaHMg`5bse*A!<+S{Dz)eR*;-x_`7$Ea zF+0&*>J#4ui)>NeHk~`txHJ{V8~>FpsIvpb7%fePzi!b!Kw02&&^3-P>=6Vy&H%Q6 zFl0**-k?ete4ZCQJp8e$vw!T{`zA&H{QCRq6UwaRYCoOfEhU8p+fOY<;Zc5=a+>q= zMA3@DKz2S93s45yx2gRxS9erYu3@okxhxU-+4Hq4Vwn!soLR-M?!P13muEWb2K2Xs z6_aHSM^kvn>9Zy{#S+srT6BEnBZvEEJB;x$rCSkNXdw|hlv{55C!i}D^{Yj%J$mh+ zAk9HoK?>}@LC*=XPi3NSj57~gZROm+%HNR0<%%WFLn~bGik;%F3ZmvtFrG`ORV}1J z=en6%tZb9bj~4ry|GpJwQ_lPuE80*;J9&h0b+z9A8aa_I7X$3W1)@ZEOb~|eg&8#S zTtbxMXvS)x{Rc_Z!B?X-IpGV*Ud?$`?kn%1TjmHyi>yy6MQe}E_fA`plUrI9)vvx> zc%!_Uf|SPI)7({nj1{{Qgmq8lJ1d99sSiS>7>0#v&NC(){aeLXCa+-uHer;v#hq8d z_qDSyk-onBL8LoMIne87*qd!Wz~{L*)Kpt=&h3dKi__wFlo#qUDWShJVP711mf%0c z_{yc;X1eLdQha~XdRjCf(J|zYIUAPd<1RAC6DDw}^_cmBSgzY)<`ko(G^y$kl{Ak& zGq`J{YL2VnsG6&$x4Z05@)e1n?fQV5QAeJc4qtm}`bRHw)_d-a_T7w_?U%__Cv_9t<+vlSn{ z^cWc13%ukcdy(f)=`iRl_KDb<)bzT%LK-xzFS9Uz>(%q$Rf<{BpnzG;#@Dcpdr{3Q z%3R=qD9^M=a*^Gb-y^JOn!~#dZS}Y`kbX) zhVvMPc=ER_2y+Nnv9@W?&PFd?cQ!)-Is6{Jt^dq>HfG6W_j3sj{jebb;MVxeH5}h7P!v=que6Qzwkf^H-^nZ0 zksMygqt-X}Mf^uR?_XQWZ=D)%l{upSh_qPJLVIZ_S-qB7LbZG3Xq&)N+2o7*kQt|} zCqrI4C7T(0>2!XMO;iYrcREN8*+uF3)1_yAsBcUXr*#YIbZW@+ zo-r2JL9iLM*~o#Pg$ALnDBE`!i&qw3TOHf$w(vg0ZANyq@06(~Fr4=y?zsB3WOlr; zRwo>c4<}9p4W|PpPQ9b8DZ;CF4Ye)a#ZfW3pYqh zZOSjt_v#mm3ISCnT$2}T(-$luXSN<*ywKjVqPg0*BFa$2&Sd4H{(b`RA;HYW-Ta_3 z8(v~lc5PiiLZv^i;77f$=(g>W&~o=X-~udNBbIyr4cid?SHyY;j}ul{wEJu3e@Olg(dz` z-#Ru@r1Cs4?pUHYkr;I9&w{S#kY?$%cZ0nSDh)-ti@!{CZ~Urv+o>*20^|w9DHfqdRiSWDvKsYtcDkp8apw z_PES;q2nf+-dBGd9K}9!h+yB*sWelFDXayBe2R#X)nW8JsmRmJB2bKt6peycKj95< zFG=m#{WA66-#4doe=fzVBYi!#Z_fp_-n|T0@2y-JhjzGGSZ!s5OYz($5Quft(GmO7 z`Ze6J_^Wd9mIm| z6>Zkh-oF<=XF*`{V9=vM;s}ifU9itA%>?CZ@wH*p04i`_)HWu%cQsGj#!whmbi6fZnL0IoYOl(&-r&Jqul#g?8s z%zfRGy3@hMyd7uiLd&m&zGnLjK)=IAp&#&7gzrkK1=_)`^^=9=t-j!u9;k#U>$UCi8Phg#kf++E>ovuMq08^#jC=rb0k-*YPhV`P`K z<8l<-{%#$5N!2l+Mv0Q4$7&OQi?%`nvm49$s6qGPau@7gd>7d-Wp}2^XIrkqRK@;7 zlHskLzfavD-)Y6<45@`A6Pv%)zgn$&1MgcQ#K28|BNd54icQYk5_@+RkKUO`kvC}Z zNZ9TWp(>Ek%h_X}YV#eAX#Q~9dh6{n)vsOk@RMHGkt?+_j+}i0miE>;uaO+XTK#t~ zJd-qyFe#;;_Wi`>C<^+%Y{cps22x&k--@L$A#i$vXPBAM84) zCt>46T2MPe-IXD~u=Eulk%%Hjq^2?xR7p}^bPMK zCSHp-j5a0nYwtxr^<8M7Ep6}pHDw8N@saq-S3F3SS;3p0A}?=zc3gMVI*iG`V|nnF zFUQB3whT1^bC~pYWLDuN{{)>ie2I;I;6&Fyk?lH5E#{vv2g;)nn?XKRYKk~Tryq({ z%RIH_PO(5cOntyyTU1)4`g6uxp8tN2SqV5+TiG*_)Y%W}&N1ArlzC%okuZrkKD&Q} zrI3Gh_BBJPaE$06=*^AAI~t95ZT$+KeUvuQQ_El|?sr-FWK-gnFDe@BM*3rAdl;*p~&Wu_P+a9n7f7=M!GhZVaZ|YdJ{w9?CN*s3MtdV?o9P>H>{9+Qe z#}wD(vEc-&czn`o>!?oCpNw|~u(L+dYPP$BvZ~!v{{@I<1NQZ*^dX1&L2b)XE$3um zuLenNbAGsVvOjWr4Y00JZMw5E@a|;yp8!(jl<#Or%1Ib=#%DIGez|q;{cYhy<73g^ zTTYQH5-sLE)?&<12^X^Os=s~igTAQuPjy9tj4~JcdS9s_ars&5kD{WRklAg=f1{2> zk39Q)sxVmjka1USFt+d8^rAq)F~!{^Y{)Rb^W9GhK4Sm$3+|v>tj>sVNsA}7nRy)= ztQ*>vJNW3qRh1y&vY*AT%ufEyzDz!}e5cdQ{9}!C0DKV!8&h{`mlHh)&4S_e8XMUAAy5XHG;K8TkKHT%; zD-V2pZ(#;9evmWuF*`|d?s}70rGc!8Y^D{Mn076}WO0<^!yTt?1hu;HW)hsN?t1%( zjWb_M#d|%qC$B(dL2md%1GaZgyv{zyb2@}}XR_Nv=G$@m#@C6mU5Joq-J;j^nM3rZ z?JJ5q3ZTN6huU3xN$s5vXO*SWlGj-9m(BUL&37s;{8Ieuc7S`=>zX>#J)O&$rZ4Be z;tWJ)MM$iPei>?U3hZ4lHCwm^$_d9vq`$+^(~^g)k9l454onNXyx;!!lMz#%@q9CL z_cOx2BD@NkoSr?l-=yE8YvKOde>-7ozPgi7@0MxlXE(Pv-?Ivv-C%(V8xtPq{^FRT zLOdrbF@N>x%63z}`tb80dRIaG9ECRZ8)@T%bAN>X4P4WfaW_?dEKltYdD7;xTVDL` zgwKH?2rWD*;(QIum!hsOo46RhSk{`YNw{`meC2H{ry2l#GV677fw-TECzJ!_pWfn? z7`oHF1p}+h7$fR@z>2+CD%~vq`G>P1yVziEt8&lwy9e7A=-Wu|au`q!Jz8H< z6@(n_&z%ox%}gM7!L~T0P#{)2TN8}swC9fuz};tG*ChbEBG2y;UY@m5(*Ec=xB^Uk zib&UK9gfhiH8HKadDmuJoDs0;)&CJwlg={v;sQ3`n@~&h8ORqu$%#H7tsG4%A3)QR z2fo|3LxzT~4vN(a1`8RAYov-eZ10<{YG}FTY0qP9%~$g+gp`rK8r`u6p4-=D{BUK! zl%TeMQ7vc*0jzs?D;k6sG%) zjLyQZ?e!skpuv<-_v!49a#C#}4CQ)PO$H*slzj8nrYMwMdgz5o_{2+``}b4*gJmFbRtD-*nnLWk{KXDoS3D;gkFFrKZlM9I{ueCDWDa&pck^$RJMP_W9d5VObHT55g z`11MZV6FW@Q3kI@9aWnet)W4gIajpPG9(-Fkfe?{bJcJq_{%CkqT>TUAtD~Sj5O^t zGc01?_%#Myd5A2yMyHoSwqRYdvRq_@j6_iXI)w(XaX{G?ix%=Wj$xyNR{)!cFX z$xmDL3|+qdc`~jmwz`@=@7fm0Hqjn(c&Wc_Up*1@b69D>H9<{KULdHoT;QD1!jXm8=RPVsCC$I0=WDQt$Qiv*H5})|l&96hsm?LimFZ&=;S)R>5>PTB&*!}4 zRp+_xMs9K5Q{D?wL*D1Lc~((2rI*YK`wLiOQDP1^{w$%4%WBRV+_>o2u{Tz`?rq<$ zMy{gek;c$c%z(QGx!T{_2a!X!?$F^);eGrNXZU(E+GI}}Q}-cf%BIH*-$^Gj)ObEz zJuV6Hs>CCMp0(Fp961T~m#-YW%J;WV|92=hp&a51Z~GW*fiGaTz2@#-r}GsmgsUPt z0;7_|!RGhR3%M^Bkh~@)oPVjnpR-iFepHj$#8R&;=|}rDbC=b~(EzGgMc#P%BIsuH zV48g4v|5LzeLF1mt#0Iu)Tpxk>ZZ7k5+g%1|E}tZBfGr-S~6?OIePturz8)$^$3;l z%Whi>3Nev`t)E4XzY=h^XUc__BJcPwTKE;JN~V@&(+Z(sk?NPj50qLC(ye~q^=yMw z;jA=YacGs%>~rHK_)UBEdS3vlEGE7Z37aaWBk*5!y^DHFV1YXFx)1wZ^fT}89)Wz% zx;F?Aw`{jd(lB?w**8i8n=r<)bB?odjiy56#1uMVFW!-ryFN3T-4sNH9gH{Pq}6%e zDwqxP`<+$RtVPnurZGixyYC-P9Z6Ow(wKv%j$!1=9zT8cHI&K4E|x0*E&F{-;$rkU z&6XmMfz0T+LK(=f(Yh{C#>Bt#@vG%Ls2g_q>go6Fri$ryu+F^X>DSXC^uMWUTy-5; zqbXL<_Hi|eN|!6XZ7v7#5h3jD)WaRue3GnFjgG6Ts_MZ%bJ7<6U7)|Z(BNaje`6jo zWruj4Ur3mJQoKibc9DA%aVtyvq~yns)9ua|C=|El70tC5PhskAfcxEyH~gHcmMWLQ zg6#b)&g@nBqf(4qY?%|?s89&=mS}WySeh;lxg}GpD-CMf^XN3>eq(BR-$yKC0+Eyt ztOPwZk|a_y=zkXhU%ufVrggfsouiA{7Zo_ZsSBj`znR*-xWIUNLHo^`6ruriwY$spF_Uh>#d;xE3;~v(;roSyUp;EY{ z7KZxUyVT}-zRF3O!8{J&*C$gY5(&{8o~A}|^g{MH_NlzUeY4xH@Rygr#O3kKd-V~V z?n&4l_$V|X#Z&L_&OGL4c2)=RLT0Xe*jqo>Jkc4H4<#5aoYf!At3zlmJC?zH8P7WN z&gY@EzeC?T%l&O%)IXbzk{FgGWOPx)yvQ7BEcULp@!c3+S|9&H= zL_D9!(lfPo2ChRL!vIprI|psIFxu@3Rm*_TmOf}E8LE+^QMM|M<-T7D$AdD-T9~Q zq_MiiJ)V^L$~yoVY;-pJVhG>ITJIVOz!Vv~lk42vWu4*>-VFSD$Jgdm0e&Q?(BAZ7EcW zoNn3<*6`;E=Q3~W{`Ek3PXg{^rXVBb5L&3IU27J-&=Skmp{81HOD=Xr(Zxr^6H{6b zIYzmT-6npPbVge|OnWiB(7c5|RB#cei@iL&?QS0wCzWnZ-(44UYzyBxC$H*#XurZc z@L@(pT&pOKK`_}lSiN=DvQJ;6TpSq+E+pW`z9Do&4K(Ew&$*P|Al`X#T|1awBDzdy zr*7iOdJiV=w=r&CBGOOl>=r;%U0@OVCw!~*y0B8V z&$7ZAIcx%E_#x`A@*I(+!ZN3?6p*AMpD2?nlQxF2Of&jpWgu<)>o&Unk=X9L(q(79 zxs=u;%D-hf6LNC#nc>_UcbAK6MOS`_=uR<2ahGB znlvs_Y{CU5>fZ(~NV2a$9Ov=My2!tM?x=Go7E=R|zUYENcJgC2|GVLH1GK-ID|&AU z&xV{U&f9oDrbWL#_3rAcn4_oZn5kPnPg*aDYtJ&XN1)=W?Tm(nbR%|g#_7j(L%R=l zbH=1#?v%mm;V#)ai3Soh!;)fklT3sh>d3|qR)s>!>@_!biJkE9P z$X&ZVDX4TF|HU+Ez>fy|&%|!*=JQUIu$$#w3QP7O3y*W(CHb~HQ$Z4>=+ye8RyR?w$TLV)Z3i z!=qnjHG1QpoYVWNN#WN3^o@Ov?Va1rX1lAxO4Q|xCisxh)Y$Omb@hMVxcLl-#azgH|nagwS3(xDwEP0+q&_QqWz<|nx+6e({nsQReyR( z;AMKQmmv%N^FRL;bZE)UFfI*vKb<&sO(bU`4r&#;x`;R#@!{cuE;nh0;AIVtb38tI zF6|R1MeX2c6|%Hmkj+hpDtGB}kDBs3pu`3r|H0S> zx)H?WN@m!IJdrTJEU`vmX5X4@>uuoql9g^#8p)bTVfzSKJ0EzWxpqWa^Qe6BYiz0o zkD$#w!HJcx!(92TBr2t@N9ho|VClOXp{5tLJTF!Fom;!!NczBEXXJzTRe%(UKHd7F z^K0*Ye`>LR>3GSU?+&pwD(LB2TYSssH2McwG{UaJuyIvTFRs^pxNc>*!E)c-5iDu2 zb6q?-agS%joXz@|{xP*8K2~!l-gPHDqti%Cs4I+i^XpuH>0C*Byvw^2vqaPT$ zty44Kk2*$3Y2ZrGLCxwunW@;ZQrtG{4;jC6FV>7gk3+PYS6|ZdwZUNQ6oZ!xu!Rz6SY{|-HUi`LUxD^xv?F@}6S+mxmkVeA4df~_j^leS&$Q_SwF zOvk;Ae3lC(LE=p*JJljdf8OXN1IJSvUU+FEy^`-AkgOx@5Yk?Pl~b+JO#iEtS0Bj)X~N2>{tjmRkfJ0qvn6Ijti3vi*eM!P#` zzAyY<(9-k(xvKprebdu07IyPdExk5}wyE*OKzl|R-Tb~83gdD#jj9L0&Dp^`j;~{HjD1(Mi7EClOb` z*8YA=8*UUlU{AmMwV$ZG@?Pf72LnIP@Pm5GxTxXQwO^uM+1`nt^yhO+fJm~#-FzX~ zGVeYt@3VL-sz~9A|Kk$STxcT4(|rcK%}Qzd_E z;!QcD{GL$DEHj4#Hjk|D*^qttq68;Y`5)JWU1AB)1MuTn-%<`RBas?C{D2pSLb zvpBKg$p?iW)R_$#ff&Y;i`j&tkkm=>ZXb4Qz4gfUV&Kd3P>-B-h39t@qy5Dx8`$f4 zro^kBt&SWd!?VZu$5Ie#CF|wX7l{5YbxF=UQtZ|@S-*wz`-Ondaa)$xpZlf&d!w>; zS|0piU_D>3p0>V~!c69F*Wa~U3jkkGT5mGP{|yXELyCOU{=s{7sPjZtA=d#03r{xg z37Wgk(Bd#@bsewDH~LfuzjXS!=fW+ERTcB-7m*BjthmktJHD1{prbZ@q?au| z^Svr^sK8d4?p{>r&{l_@_Gd1Xm#ybaBt>@XsK3hQf!%(U@Qv=yMSdez^VS=U4a@al z;$*VKRKpGAjv59R+O7x?w;mwC*2OAEWgp7WeX*oJz6(j)5Ni^rMlod3yo_u;j`9`8y5DGS(P z6_Ppa66+Kc4qTxSap3wc*C99rwKVx|}&lZ@V#n*0x zVBtGV9V~pPH{TXm-c9*`9Qd)A5O&Tea!IM2dQ*j1+goM}tnB9PzNtOS->!${EZ|Kl z>-B_@LfFNlq~QNKh)4vpOGjZL|M!9F0I>U?LRota0YQTqq=ts|YQUKh04?u!N6J36 z%GzAWyHTyplxGRe#0Fj4_)CE%5H_`Bod6kFFcgxqhuhul#(-NyByMvX{Jr%6v-6xB z7vw_h)XD1TO$&EDmj1@+i=fN@rx&i~*GSfE1P-@ z#+1n{`aj(UWX9275EToyCTWnUON*<2HlE-Zr_HQh>T^-P$Yyv~`B>wR6<=MHOj zevR+{ukNP9gvNo2)o&8$!I~NKcrdCFcNYfh18`Y%S$dyp;+~c*Ms5LutPJ=mW_%WgN4GV`{RsQf_ zGxXohQfeEY&I0jLU;%3`#Ucv1isr1p>7uPUuGOQ2gIgZ;{+AxZ?>np2yn_asz;QRY z9_oy|!P@0kJ1O?8fCVDL`M>1bfAvIx);dXe4P4!il7vSP7Ke+4Z(J__Z+~aZ?}lfd zuhiD9IldaDD1|sz7U9N%wAJG1TorL7vpn9I!B0r%p?hjiJ4OS|jmkDU~cv z6Z-=pyIhA|&x1de^xs2T9o`*>Ru5rDyGQXHssNj(F2W5PK>wa&TRBDX<-Km`7gG1EVU%D(FAqUKriBhQoJdj#09PK~8 z5>EI!it?>o>L#MC9#wj%Yxp}@bSB$G`5YI$7_TS}Q3mUk!+-cXRjwMPl#c-^t8J$i zIa@d^XPgx@ibkzgL#%9>Q83$1ZE7YV)9R6JCq}#c-2sNpMO=iqiqZyhWts5Rf7zB( zw@25FUIg`}l8wp{_odPihK5eB{%dfr6Se;w@wBg-|LCYxp8$x&8N_`?Ok_YTFwM_U zL@D+$40Y;@hSaAdD~#rvv?iuRvuKf=LHNoXFRx)QSEH>`D5Y(Y+$D7TCvg0RkQ<|t zfZ+D5xE!rvi>xnPS{29 zkRUVSpxWWJJ1$^EzhXQ^m-&eh-=yQ9UbULJ{-sn=r*a0mKnT;2DzqCU4(PJb{`o_39$gZ)Wn*h)Xys^Zq`<->Ettj&(z7SND{flm z=y>%OrxgC_pt@?3%{aWtrY#h+b_lt1MZM|1?*5}3!V+MV>-pvYbDA-2i;S)agURs2 zdAQr5pj}U%Cw@33tjIWV*kEG?L=af^A(3;BYWdTrpzM{|jXOg_r~X`!pP1~bi{{Km zt02OrAGax&E8)q?_}I{a{LJ+NE~5aV%PiJSI*zBh;^V!|b5MEqX2J`)B;PJ847DW7 z#<`auj#?E*!{~2dM(i#wYm>nV;Z_nyk>`$Mr{y6Q$k|Zju~gI?8tJ6&PhC7)k6;g? z*yQJGHPY)a)=zOr%Ibj>j}F)BpY@?UY>qabqHQ|ssk`Bc#?jTdGoQxR8=8)%!~fjP zY*Iv>nfcRz>)x%hDh4`3}G}Cxq~A6y@XFx5v#UzL>T40ovC=oc~rZ z*MG@7!)k6z4fJU0XiOE^IRuVncM>pJ$kRA}n!Ir0qwRj{qevvZrJU1Den8Xo=e+CE z|0*mDVs!Go_)?zQ^ho;i_|4T9Xk*%HCw#n6Yr_a%f#4j~sbFj=hW)u4o|t+X(c0j6dNSVk z+O=8(0^c*YfCVcvt}~A6jMn86xvaCLZ1hG-T2XfM71}sYG_x0O z|7Psw+e;qjvQZW~b7f5`ZFC9B_c0Nir5c!PrlYG`p;Nt23~VW(>H95GV2D*)o#wx6 zG49nQ*=EOWbnZFws1p zqJgInXj4L9w`y;wxcRkdw{@g2&C#dJ5oDeoK25}e0KNplb%s@pv8Jui5^hytMA1;& z{fm0+uTVTB|8nqZq9)>U3kzVtR2M!+nEz32#m`nE3HKF|fxx(RIq zWq%e3L&JGUgHo#`pvUfQEJ+*yJ6rWy_q51Mr11$un;D+}k-X<*^*)x&o%@2Ap* zdn{D(3m-H9FkC&~hEVq`v96fXZcD}yah8vKIpwy|md>Nq+#h%gDL7p%K1qscc!AcX zyZ#t&FgK*lS0;S?(=*lg8X6Y1{ZT#@QN`-;4M`Gg{0X%fouo@anb6mOqpb(UHkmGEm$s5!rz z3)DZy>sLIQ>(o#bwE`UIBOMwrZ-z#(qPeS&>vyWXYkm&c%&YgK#xO>Yz>+vfc%4Rd z<5CGZI7SY%@2)>N1cI0(&zs}?wAZsZeJP9=T;2&Ns{wcwC5Q$1%(0Eg7K#Z|5@@IBDU@Sb)XqAyx&mw$Wq zQ0KlH^tMS1WHHil@p|bMrHYc8IjWjUG-DZ8_0E6$;{v;Ca*&z|nDqu%glNsuQ4tm& zc=lxddkS=@LttKo;v6_E9Dn(}z>J;+{>C}tu_^pg0czuMFOjoOXqPR3;>r;#R07kM z`J-(LNs0^9;DhSS9`$g!nEg3aD?K5UZ&*Fn@kz>|E7gqEe6s@U%90vNIlc(70cK$d zEzuP^>{J8XpUd^?n)dSP_z(*^UDd!fOk>pCGz83QNny3r3IJz=(wf|o@|oisJ{#5e zWbW^oUBgkG)xOZ}u3jITdQ&)S1Ow!>!7bmTOEpuHq}zZlL0k=p7_&Z*Bn;wI>TVEc zCJQ&Y&zbU24 z6mGy)ld%52%uzhn2i7NG$-))_;V-%>}g$-Bp)D8sNv`x=4EQ%e8t# z7l)`^*Ve5aWAGzwAi_QLQ!5mANgst3FENME(AnHHlkQNp*8F$tf|{k)@f*0QP6E3; zfbh7!H>IPS56>Y;z_6?_CZ~(gz0w3_6L`*N_(Cn0=f_JJSKc%~Qa{G-d?1Wt!3WB; zF3twrp8rAb)QpD&^r0ar;B+CVZnxi`%+{hITW6lZQ#f-eSlUFN#Q4hd-79Q7m)%B0 zbP^BAPGO}$YaepJ%tEjgl97ed5Kg$pGoH(M3ZXqE(7RAbslX&6k=a)-Fr`ELB0RH( zXIG=(^!6@Ry9HsW8V8h@=@g=w_vF_QLOQ_6s}}OLUT2w*P+X?DIuHsiQr0cn;D{!` z2K@Tc1}LMFq&zk)noA>xo7ddyk^)y0FO?$~%B7tBX@P^rw&d7G0Up!_9EG;JF^<-- z)LH7)aiw>XSe*nj-F35a_cmQtDECCwxZgrv+h%-5C|6TDHPHq1#DbR9jR5>ch%72{ z^+|!hjg(h$U1&F!V1>1(|AcUUmj3WIz_a2|K&d2Y1G`b{93%Vy*=UYvBs+2!Q)=P+r=kPU%eH>jz~SElUtC z0at?NtHy57LAcOSmWSJ)WpB_#L^DWdSWQ=v|d!P``ZC2OwDzDqdZ`=JP%vk zaTlRw4aVLW9RG8~W|PTDX>2761-kk5Cgm^i)Cse|_;RhuD7do082^~IVN*dLLAuKL zwwZ7q9R%b*8F+`7Gr85ad05Vgj(xu6Nr>&pdTk_;h#EN3WO(gM0jzoMF{8MHkd`G%JRn z57D4=ni?a-(L|k%O5bB{qu*fsV;tW1!guJ02Maj!1&=1FR9$+UuCFNxN(+V#X_CI+ z8CEDo;Sfp1{YOiP{N}OULFsn*^BIp?~Mn+o~d#o$Zei<eA`yAZxD+$;Uqi$LCe{HE!_51;m(H{`kQ%{`6pC|;JLPRA?5h$2#6?hh;!VcqYb>Yo9L9uG`@fBPBKD+MX-j2#ZBWboa!_%w(j+PCqTR{;|)p z^w>49QBC{~G8Z~hn!cl*E1nm-`vg}g0l3$PGcrvn@h>HG#%ngdyHsX4*ehS!I%7A@ zJ{n_lP(C_d9ChuipUK{qw>RRnKE4o*gS8w94&)=A6W6G=1|@KtwO2E~3}2LZC$e;q zeU4KQdf(N~sz~~ocxxJ)(7mUZOTccaKr9ZNK`ujI3#7^IXg6pJ36s9up0>42F6JJG z`{Cx*aU!r=2XjLR zN=NZjlxBYK^vn@Z&B|Ay;s^-#+@d)wVBICjwX9{p!ZF$9XG7TVCIj%kJMk$eKQZ~q zfQ9tqG*6b{*V2r5%j0^U3aOT&^^sEA2aBm|QJTxmQMw#jbBh0R<#2_^l*}osa1f0E z0p47x5=Oe-SMB9e(tUY&m*(QDyw6{^i>Q+}aNK}+hLM5j1NjfPnq>9GukzM zBgieQ+5zd_*#Y6LaDYaZpy6UU8DGVG9|7X`4(yy-zMP!#Qjc3um^lOc{i0B5Ic}}T zxLEAuNy&u*9P)*rxs_|_ZKh&D&8ccGhncnv(9i4fdJzEuUXI8DEaPFkjA5qN?Dr2A zqJ{aT1C}R4n9J3WRJF{H0dpH=VmD9fn_o5XXm3!|x463ShCjh*e`4@wCumk}^2pIu z`w_+LXcT(^RtOC{mq49xNOIj5&~*(o!9`5<@lB-5d7 zMz_L&Z>v1pR^>(;_o>5+x!5bpTrSIBl#>BIaOpNWYtHIpKN(_**7@1o=<;Ffp;x6H zS9q(%i%hf3IimB2o>j%EaXy=u{cIH?#;~?N*HkvTFTQKqM!%tUECs<@(JuJtBU%}r zlC2`n?#O-+U4qUzQtX(5iQhSzOHO<6BdWv6y`k3senf7KkLuwKh+eFEf!y`0ctzwb z#Fx2gr`MW>3*bc3usxtThr6*QkbYZd+;$#B&CmD>jL@6+PlM%oR^ z#QOsF@>xm$hH;Y`b~Vx^Lb>dLfY}kyRnwjqkK<+LGPe218brCcp|Ns7lk0;pusw6B z9HzHY57LbVoe&(2{eE1ZZhQ|bH?Jtw(j=yEB22JLm&ZP*L)MJ7N2W|bAXyc|6 zZ-O5vkZnaXJ>$-hemFcBM zQ%6?u#%(368PbF}HBx^G>JT8^wH zM?2?N9^ubYNNI>S@_HNfGa*dKdH+^Vo!Iv)F$PjC1J<4$N@3SDrHs51Mlx#GJ_rXK z;}6cZ<+)>U1&RLW$oRiM{TOn-8gzn9>ziF=n8$(K=d4W!~?wc6as0ats$9 z>swtPCEP1p!&wIo#z*vB?29MHy>~s&LG6{c#Tb8Bbm-_jf~DP4C6~0q;#~W1YNU~) z+=#q-%10kxZp*W^m~SbvoQQ4;X|MK=L%udZE68zfY?QU;{;k<}$^Pdtoe$eE0S*sU zz2Z`Wjncpd9Nz+$hVY1)hwT?u`JC^?av|6(!$5`DdlfOvgoc1OMso1j<0l38QHpje z%y{sc1u!sN?&5$oh}M_Bs43wEeLwW4z)_Hd@%b6{0_4M(%WBfIZAX<417xZ=+brv{ zTvZBd`lyBIdd3uB8#k%-V9p8b(UW&Xn=+;_{>v><8uK4%7=BYQtxn*le^d0DX1_Rg zC1jC+hIOjq5tPLtyJ**fZ_#<>;~JVjU&zX&;*;UyZzB(!K4RRx(Bmd^s7r_G;{Uoz z&e}M&Kn?`J*acYAw!jhvwWcNJi80;7hPyTW8G?2zYT{S z#Ux<8H8=qAb`Ss~++WL=g?VT!*KUk2CgE8Q2qEJJkdXt^B}B=>nWw#`10x zW1KUdOutES9Vu}V2|B57a8+biFE=Y~Dxr}*RM;hF7yxWuv54$ty)!NP;x;9APtf={ z(1|dM-WVC<<%+4OC2z?MgAvcUP;22|JN;$^4+fp!o;)M$pjLhNU~jbUDT(6muX2^y z_I>Hr#!pMqZyRr~-ksZF+N=MA`>D69aBro}r>+o}Y@_Dk$I@10hi@+zai_;y_vtM6 zy>_MnuWhU^CZIdFL_Lc;e^R`c@F*iKuThs}?g%x&3Uf7joD>8%)V~W5OGNia++Org!b>k?NI*e@aXK!TChnA`Wbw^b|SMPnzk18McQv@P3 z@#0WIL!{Au??D^5y%8%u%F3;_)aKeH`WfY`ot`v+k|?!N!*htR5A;}C^de76DE#u8tcs!3TGf-*4t zxlTG}nODO~F**T$dRMRV_SwLHoE^1`)=o8QIw`C%`+m3JKIRp=XQ%p604SLkg_K_h ztfhfFR7YKkUu-*vpsn0sl>wVlKJ1WmOm`xXPeTx-xf~tkzwDZP``3&Scr>5Six{Fx zN(cae&tX6-???`IM{u$B^#(VBohw1g3Ue|>S4z|Q+9NC}B%={=nd!Z4;XyD~+>cF1T9DZESjk6bmypBeHgQiyg_B{kw*{Q;Mv1;af z4wE#U_S{&Z3q6*XUKT;W>VG=vTmaqW`-p*e8xE{PURn$h>2kb8!3~WJB-XbEe5^?D zf5kyNekGl`qT?=;bnn2CBzHM^=_~K*PX=@aAUsFbT;{Ud?lTKk*ZX=d{Y!HE7c1<$ zdIuk{;VKToc#k+Gk7P8%;bR^KujPYm!;9dAH|AwRpg7PDo;#v`@^@jaS>&QA7Ntu# z$m$brCee8$J@kFU#wZYC#S8$~H;vT9$5zO_R4Ij#Sj9lFT1yvpWAE1$&fd zGCK>yD67oG)iIXgI88mY0`L8%P0zaD>a~aiF1!bHYZNTWBv=1|Prs^rdg0E>#21S; zGpP0(=l0md+(Nq<L)x;HLWOI3X9!Rx@Uc{BTx zicF1Fr0%se9FG(Sdt#5;+AY2qXbr+J$&dkA3*Ja)6#Ys^Jly)hXiE?ib3wZdcS}o&v6L zD?u;fkdqYfX7wA}gw0-m6+7$pHCWK}hiNXn{bbYqjWJJX*=%aWbDvk#(scGUQ1ggf zR8C%*Wxv8^24W%FdQ%e#P;p0LU6y;cML5V%tm%51`d-gH8{K8?Gjw8#u=Im)MpMjy z>)LF0VW(5H{M|bm2Sq?VL;!;J{p=S@CIgW;>s*f5u-9*ZVw%$%^?mL^7p+6UwmcDZD8&6ryoLq5os_^6~Dq z*!Rlqadj%$T=M6jJrvxS##&C310UK3yAb58nodj;ySn*g9%i?AQct-I|BGI8fxi!D5ouxc9TPy}Zh% zAKFn)3f#}DJbzO1f;9ZNpu#BmM2K|aj-!fiS_EdB}ZwIn`t}k0fgz)VND7<8u_53 zHmj6NA|MEs_H*}K23c&_4G1YFFuZsA123av744_mN=A>$vJ3%3nN*SN&AiqY`xz3J z4bU!#-gH?3f}m(2dT%eA%_8c8FE)y0yxJCMG*-TlUws*7tWtTO8tI0Rl_`^#KGc+f zwoSD0>ei3B_VcS)zp|uco7q~O+>xAh;M#ItR}GqUG|QkOAn3Wt;A2;`&@e1n6B@q5 zfWOYUpth{mNEDY*nKAXbe%Hb&-aDdoBHj6E*AiigP*L5?(gUaN8~Wqe1Y`INI+env zmg6F`5scoHT`~CP7}?RRfkf_>PS>*GBgVKmK$%#sCP_UheK|aCG}+b*)lCLo;fC6Q z*(Th(KlIRJ!`qqKNiqtVvihK_NRPfE-Op#={4aU<=c31L_igF;8PCzW@U1&4kO}+ zZx)4k;~`y7xC!iL;o=M{saCdx>70^yb4a|(hG*0zUtfJ6bnG;c7K}H_k*zCkD>%2X zkqe~?!$Pjk_I!iq-ZMK1+ScTT%3(V`ZnARGZH}ym%5N`^1-6uX1s^`emEszM_o8A^ zM*RDE8{IX0<24hxs@hL_oQEG454L+-l{&I=&i%~#wMPkU4`NeC3v58NA0%XXn^V>? zKZS!Zjq7Jh70falHriCGb8SFAK%byPla#;9>y*c>3d4W|f~AeG$_jq=2B^s58*|$h z)gNBHN?|jPG! z`jOe(C>f=Z002p%KR#DL^^bcKUZYKyRK~!+wR$P+4X$Xw0j_ zA1KL8x4A;XGM9m%Mu@jk`2=uf!Cj%kV5y&{M&t@d9Cew)%||?7qF44s%jqo~6Ky!6 zkCRY)WSx+8*5qdk#~V;1oS^cB#E+(F7hflGPCW!NQw6;Qba1-lmcaf5@5QVflEM`S z(YGckI@(qqjeCnrkaNK5f!tLEE52c?%#=OIkp87^^cNEJVp||od;NwLd`nm3c-t)<-$mIU6f;-*|Z7t(!wv z?I{Hb?^(6t_>87oF4gp5)qs1d3qgB|_Qp$Km}(WGHzDf#E;h<-z53R@T85Q#jC+wL zKAt)M8_!H-xr2R|iI1#A#qvpuwiWv{8@Mh8Wp~k=T7T4`9&IBhp`}Db3&$nM>qu{P zfu}0#A+uE~53$HyFIZ?+8&_?rqr7Nt&k;v~0$=O0_RpBrf@(@}?XNA0vX3ViZX|Z= z2xzl;FkXg~@zGIoGcXy&9nmkr-q=)(9ElkJM*}CfTOHSBE*2L{JgSfmh&&dcmt*Y9 zI<_4IMzL1+YYwL$LqclTu9w~BX!duZI27+f+fNHuKt6yL75v~tKWfex7C!4Xr)ePi zT+*XK#3B)UV++0=)<_FL+vdXr?Gj34<6IR0x z%B;)-;q+d-3_ioI|E76IEFO>_BDBEeKfJBt2Tn^W+_N#~HF5EmU5WyopfelxK<~?5 ze)%Gx+vCw(d3T(NhC=GPJZ@nx$Ry!}G!uX+Nwdw$hivQ}56&hK7ZX!ZIzXGv_#PLt zTHZPtdCI?1S^tJe{o6_mKvz)~rPV-dJE`+IjGN=q@YDFd1w$y&=#2Q4(mu~)L#ZE6 znW(rpi}TH-)_v}9wsdqbv-Okrx2sMrx&R(azc+Fz#kC9nHWINK>(UpSR^)O=RA%Pc z45zeo#wnbNZV2?{|0-vF)v{{ZBb{z=r8h=VzPJT)tus@p)=I^Mg@;Aw^?8#hSTyIA zjpkgD%d}{OJ)eN^L-F5^1h>?>t3CgCf20xRt3MhjSK`v*(1m%!R8tteyJ}OkGk(^B zTcxIHW&E!dW#C>s(=$_ul$UFbgI;@ z^?XDD<(dQcvH`j9_oS;xEMys|8(!9xo70Zkw$%0Se+mPKw$mH}mM3jmPFE*=RszSmGo9Z?K+1)^WXN0hj&&t2+(m0Kyi z*A$en-AqnLrYf(b=ZZ+TvPa@Mk9*z#*D8%TfVS}-R$6cDmIT<-p@b0#z^RA(&0*Kj z7I6N@*>ThO%?iRSM6mz*nFg`a6Z)M@CY~$hjBa>r*A&%(1frL>sCk)YcLc2Jv~Gg@ z#%7|mnaV{zKp1Hy4xH$SmKBta+kZx~^yS+{DxiGarhK>*>Hy=A(Z$iowX{O`p6bmW z2@6qZrff3UY0(Q%J$L-E>e+Vzknd+u^rBG_&;idGH@EhEocg?BnKbbZOES26q|wW$ zQI)uD4wnBhqRKi{yr)#I!k-f9(wY-jtfo_=uexHFQdeWpTgJ6ciA1b3T-JWd7L`d~ ziuF<<;Zm24QQ2oh`kjrsg)W((iE*$w|Kt9i4pc^Z_VBcRd7EWC_kJ`(-?5y%!C0g! zsNJs@mrPbZPmXqH zWd9m(L_a3g@dzB+-Q5c?=1L&fXfnF`rs38hh3$Az<7w21qWYNNfK3V<8rumo^QyaV z_A)aRtiXH66hy1Wv-7nz?lN-FMh|WuQWLD+4Lb4xs4f$e(Kb)|Z{F9;$S%01IxJTW z==`Z#f?{SCO;hhYJ6rN`97dFWGB)c$Fi=0YF8g-S8Ia1S`);PRoX`ht656zDqGQTH zsee^__1aP#=yMw0D3@B<6`OMC6RlAwG<6F`IF1E|F*%B4mzi%2dLFP>CNG~=&L zi`LNql!Wz>Hpcw#1ZXY8FiK&&y}7~2s8PvVs)1;@w;1S_T_R(%v^q6*`1<|h5?Vg) z{?pB`pl%<&&Bv87#7WoE-1?Be&o+Iiq?jm&#vUR;^j-p?`&A?Z5^ z=*7kXmv7Tf!zfq>pFlqCt&){raru+sxGfvC2m|kW6!OhZ>wE5HE6w^v@j>e8>I|H_ zg5X|?VaNyGt4LyuP!{1UZou5ZepCtPDMc@Uvlm(_N6^2v8g^V=kbqP|hM|+u+8h3R zo|-*i3xG|_76ZE8iT3Mym2!a#KIz9E8|_aBb~Ea0jNw=kfcb^}-F?>^YJLWRfd}-& zyIDHa>yydX9Jw(+_WEFPr0U2qet7Z@a{Hs|rTALf&j{&QW06q}DTP>D(cTQdD6tUt zlmKc=?1N=ZJl!N7d9@lKH?oaKGUm!l+aNeCy+Fw=4o>3=Rg*~O(uV5EDS%oXh~aBv zTxE}Yc4==`gn9-Sp9-lo--*S-09rCELw9q_21m8T9X0T(f33?}sV`P88+!VQT9SAz zJlLmOU))}yXY#dz*~n-{VGjd-7VVv8%emgMafUVKV(T}Tu~^b;(k2CH0n}q}jMC9E zvZ!QVr&Qikf#?^`wDt`6@hb_;lPalmi% z+Hh_t2cwK#S#cTq9zxOeRX^t3f?8=MIM11P=h_dnyD-+~hSQyTi+P+2&^sAJ0~T!w zt+10zg5a5vrZqte)zxpg>ja!bM096}cfEM44D*DaT^G8_SC3@-ShQGl&8IO#;>hT^qBOkV9(!0l*Q+}Bomb@r(-RyB>Bn} z!oqpcsRB__+Eps%fLI;I4Hwz5FrVA@kStn8#Qtd8f5~zmk>bl1p7t+eYh3zlQ5us>^`J+?QPY`6-LhBsvV9 ztbUgB)1mE^h(+5)V@Fq6^RBH*WhMivHR>E~HTLi#zCbrYTb)q2_@>;pIg`N2I2Ix# zeDJNZj!U)+A;i3G;85u!Rd3q%{>+DeKrH_SUj5$;uKg1K)@dI4MbJ>M2#6@Xwd(jE zG4(&xGynPzX5su>cO6P7)dGCMH7_rZ-Kj45hg<%C7x>SeLg^#_R$-}krCJ=?lxwkL zm+fx{&bj?sDEeph`hRW#=W(*TMLYs1K-+mf|G&sy37_G~x5CJiN^vu5c1Mm9NJKek~BE2qs3D(8B z0*q7WlmJoXKV~E5{1-vA*|g}gj>NM$F!jvS|FP}Z^0sc`%d_HmV5k7%dfKtm`jz@Q z_8pI-RqyY)r!C+)tKuz{8Zn^)>*o-70P38#75uK&o+ufcdX zB~b2@M?7RDrxi;}%cAZy1B1j&40&CuQPtJe)y6x<%aL#Ej^98-^2s_%H$X$7pCzyS zfwKQMQ~ZT>Q1yX{eVz^edo z3P+hbNIkYO-~8j>z95K~hpU|dYRLU#8)rxdS)v9d&OW@MyN3MYnSFrwyex?NEFcr~ z8LaeF<@ZaT#~uA`;h8Hg<;AWmfTPb6`Ncp~%9bAO|NTZD!M+Q1C}=iceC8ip=mXKs z?=%AqrEfXDV{`}4L|qVHB#+ zC+70#$wwz$y`&<`6rih?P7(|xkz~h$-{KA|df44LL=VNoxWv(Qy+q*wOjX`1q z|7oV{UWD?kjs}L#;qO41Wn@a(?%PWz<7fV7LIZj0X58U7{{F82w&j1la2@OX`)=@V z30HyQ%P2$F63MB3y_bpI52}CCW|A(w?+)mDQ0?b9$N7X#p#I_p4+B=vPX)g3VK5^T z`&`-(G9;t%l#)N)&?79QZ6 zZI^OxX)a#-2lM*dCc3_q*!4eW0sJ+52=NA8=CXg^(0yEOT%z`R|HxhxyiO;qX2$!a zq^9?y{(*IoAct+BUMV%+J78t^;S<|IZOym;;<*%A9;x3VJQe+4DUZAG1F!;-03=C&4kV@sfZ?2Lr;_d#aCBH}nkx1Zcz z`mgWc4_0lx-~YT}7QDyZyV| z{Npzz2{dNN1H5%>qY1#8 z?|2YZgL&&Jy9-Tzcc^`0ytV5SKm1vM2TNIPt`%5QIcM_2ThGTXe{b0BZu}1e9u@TG zn@U?3Z@Hc8oJVQElyv*{j+a#OC)RQgvNzfk6LkW|z@Y%%gU$6* z{^Dc>f@c`-t(HQP#*;#M>&ESzyF_1|KP`9+jSY10b+2a?ab@;_w5Ut4m2qz`by z$Imh@eFF45BTD4%2LqfOZ`(J?PX2TU&nckJ*R9*0n=gfE;H|_vf1dlXUOWVYQv%MCmKjD&47uD=SBRl; zGYM6QO>nF=Y_nB5=6^S{0(dv{0pKXE5v|#RO}BBD{cdJG46$dM!jjA>A8uMfg+M|t zEiHw%{Ai>0`Mp^RwX99k=DdQGxj8v0?(2U`BCKckCT(ea_aqOF1@KhWWSBFb=iSdp zHlLRu3@p<7tD^lz@?FLwAG?&;yamVq(tiH7hkv)7b*u|_fMhgT_sFT}u2jI(FFf0- z0CJ&XO+iCL+t%e(#`T zKs5S(syJmxcRC_j0i-1QR*jtNTsy8hl0S0Jo|#iVa&rN0o9*`6s+efLpfm4OUF56Q zTG;JvkNhig{SwXp-m`#^K@s>p%1dsxm|RGE2^yMSHwV_v`{&ng20g$w*W}I>1J8)~ z_6E-#2;}CV$nIh2V*TTzFG06EGdCXIPVqB%P-0W+@re|d?+<($tnKj6pfFkf&bSVL z#k{6}XUV_+TR>?AJV5tW&L+Mrr}5SuDZug8ecSgQSDTs&Nu9*vyP$13IZ=jUn@);qJjnalbK&d#p& zjaNo`N+)1? zmk97E>-N@%_|NN4W9GAFVYPGzrs+b}nb9u4%6&_1GZt>Xg)p+dP9vh$+n)LwO~0n? zl;=IPadYG&Oh13k?AEwBuv;GCl-X?LoPh0gTaV>Z>xh{x3GuL;gsHpAb6D|4{Be+y zY;Q6dqfyX`_{e4{EPNq5>^_!n1zn0OLhdif#WsVO_vkf=1y9B)^Q<+iUva(v0nxwR z-liq`ZE~^kNlUkaj@_VxJisvN{1zrh2IR7)-)9MH2A+d$Lq9A37EIOF*;(J`l}Rk- z+smb%Uu8f6d8L(>*|VJuR%Z@kAb#RAnwG0LH$h`u zg6A`F;PVHJ7)UiZuhz6wJT9ld9DakA3i=F6M;Arrchwv@^HH^{h*3m-*k(Fy=7t^8 zU2f!2Tt_E-(CyrtjVJ)!2`1`a_Ior_c(-#>SD(0b1t(B>zm zFc!K~VxKR=ah!HR9U1Jw36i8^Ob(O}L8f9)p{SDd0_1v5w-?Yn5%oIYw9m>!bA2?& z$pH5f_D+pj`{oqVZ5@hhk49Kn=Gb(T?qN6)ghnmz+T>Fz6j>Olq6Fe>o`>-s>R%98 z3TM09Olzp$Z+>-R(yyJ~>O1GVz^TtRc3s#C7 zqddyWrVT#!?;J{RGU}Hvff>J*0OK6JGC=wLnk<1~s5_l@ihCHUyC|?&xbWx-cb#h1 z5nY6Ib`6zezk*QqXm$#d^`rWP!8%%1BkuJ(WwqIBy|MwD6-otSK#V3)oA7c^qz2+Q z+Znh;Kw(V+>4Y941AAKsM_fpTpYi-@$NWVj8%w>=ZnISuL%rk97D7DegHc^zxuDNq(<*?;p~Tk#jaXTL+8XA2VtqK?`D|GJEzN;v@r?)z z-?x&Rrttd{_xZ;se%NP1BjQ!i!pT9K;PcjSG0?>#$KHnvmU-AuYTH3MGV`tPeK=yS ztpgbY-b3BO^q=RZ3#V%2yC{2o=l6mxJ{rGj(Pdd%;LyeS|O&3 zo{_J`W3sdGqi#5DY<|`Jrx;tHR30O0x}AAxm%3bQ7{~|cJG_kXlQBEFi?i}nOX2DM z#niZiI^oopRxXw$^^joI_9 z1C)9kF}_E7fQ}M9#%AHYM{v&(y(*vs#DD?lWS6f@?_ouMRnfW4Rr!?qan|i*^u#tE z(S@?jrXzoum&rhTc}!Cj^2Ib9sL|`QO${XX&cV2hLBq{HMEzE*1<-9P+tu{_!=))_ z4=MLXFuBAIK`zZKEmIuX+f?wRCRLKnT5>~0d~!ofNyKn~lLr>>R}EM?>vM8}^N78G zKHcb&=CJi+Q#Z;`;S|LgP4(009h5J@h+6i|;(}7l>S|Vj8LL5fz!~t~?dDRzz8F?6 ze@1EnhXH#5l^@}1V@U0p>oVazq?1jD_7H$t4k8_BY=EW%!$42c&8U z&%x%79w(AbbADJ?y*OU+diZ8emZUdLAH%q3sRgMJuB9ZCc9_w`3!Y6|WHpMzv=&9q zY<9ZDWT6%X+68H9@9od`bzyA6)U?q^5Nwc-kg&#lxUE~_j%ZxFs zgcbme%`teO(j-e@-9;`Ei->~X{Fh1xQ?-|LIIGbHn1$n7m8g38mt6C*59kZ zJ`G_tLSz!hfd-^x$GRidGHGX&GoUF`Qw?#XXEB0M~z=!2R>6AH+EyxY5ac1`#R}2G~p{ z{LrT2H>d#0b>;Dq7I(JolZRTCOzGdV1>FMO7D=y*5dS3db>(6iZ(ZlRR;t7y=WBC7 zPVIQFky7UAnt!9q?UtwJ$57Iki+^$ctiV%#{;S(T)tSQO`OaMPaLI+M9MryKoQHP3 z)P*qKy72QND#_Vwho!jhVn=T>cO=7EuGszim2TH7&=b64?3D}7!RY;*P4;( zhb~BNoZb@n>eMf@>Hl9B`wM$p(+0pKpsOZFWD`zCz*_=s@-BnpuO*(zHorUy%wVPB zH=8MXfW~p9>oRUDG7t!3AFS`_#jS+Rcvk}RCA^W9=bc!Yte1_h+vRV2=t!5(-Lc7M ztD0g@78Iv?1bNkL?;MG>(U|PHAa{5%VkrVC&bA+~so*w3kSD8T9(Ne%kYk$n_&ygO zwFu<`eTk;HPK^aU##bST-W8nQi7ybAG#H4X?=}N8HNBC32Q_Su4iSubxjC89TnyRW zvs>C3Xkm+D*rRP!d*Yxv>epq??vzkfBGZgo5vixR$7U3oAmrL)RSjDU&2xJ^W`O23 z*wsmA7}GLcFY}%)Fl^*Dr5FRk19R_sn1HLT*k=;Vms$&YIGTZ_QWJE(#m+qb9xOL2 zz(Sj9ziv+`$vN8?{gB$f_^E#i;tjk8N|!acpRqX&mp=Na+BLmzN_^p)?=3Cp?)k^s zwoVyuQWaw6UIecJ4GAi(U0RfO!w4dH>N9v1y;||;o_MkstuFtPDMGn8REYZ0Tqz4X z@EZ9UvJg!P@jG`)SSC9FXfKJ%Hvw8L4b|}Xdkrf)cS=?LHm%-eIU(K6;8NM4TDI;GbA2YezK?i6_HHGS3m63|te^RFP?NQ`O z)Hi7YvdzyUIRqTDwX&-S8qThdS^XNudPaF20M+|e+@+&=b>Oz$&a1K++Sbi+!1^V6 zAe_(0>D45jn%-I~7(3)z{zHcQrT`&-eP*Ls zHd+s}?G+FRa54a=;Obmoj$j1|RD20S`b6EsxJR03AiiTSnHC`JZ6cmcI09^@0ARzV zBX&)GUkYi5Hagspr>kas*MV3Sp~ZZ;&_CT0m-dd5NI@+Q6`Gpha_z)sz>Ulvpi3|{ z+%Q>f+{bQubMnowpmHYBaFRi8gBV~aX}KuYj;UQ>_xB$Yvc_)w+j)`P_Z__3K}i?b zOLnT4KndecH$XIM>1?m)mOxK2F>SqCTB-`<1m#{4fF19ByOXZd0GQF62r|x7V1-lN zXBxe?1SSi<@laGGjaB*W;`A%!070b*wzzx$G0-AH=Zk=C|3F4A2Kf=Nmj&JRZ69}Y zj)8*j$cOC7Xw)L~Ump;-yN~zqmcTbesfySlu*fIr2lsGftxHltdk}j;ol(kyM}faQ z{hEDq3DFou*%!5KefO*5pxnzne*d3F@+ZWJ^Q28V!rx_K&VuKTcFBzF=<;@u?}c~b zO};^JU@~#rmEYs$=G>U9{Jm=;(juz^Kxp|WQzQV1-{bLcGhOhLDO-8EE(sqKA?We? z0{X5j?#osQJwM=+K^1V=m;M#GlvI3;&!NjUh!eO|n)6QE!80B}f%5X+m>?_=VDF6B zhokg%Gu8`GlHTGlgh%?Gqb4tbgL&XD*E%&xFU4&2Y~@ag$6Uz=Y8#(rWkf_Cy?FT) zkD}FszeZ^xUlF$~{j7NlAq*Kf(gj$CZ;!ISIBt@NBbz<;>A(o=&Gex?FvCr zqt3|yZ_Y&04TsfUQgUGI&>Tj3=uo}ZKuO~f5NA;1bjpEU3n@w%$Xj?z0zYqpv zr9!=QpfmDrmX=8R3lJqp7;$(W1^D^pD{>#2*S-oMjKKbjh5PN}uBnE-rra-V+!vws zbr-*}`&Sf$4xB0?M$LNn_)7XF-CNfo%NmcTn+<`baLk8cm|oaK zaC79#%~_)7l1wz)5CzOD>!xBBc6^nRIV*A@*0{uVbA{IVPO83%ru~u|5AbRFiS^)P9kR zo2AQMx$B*3{Mu7Z(b11(UVEfW)Br;+^{L3lz2+~)qE;L^xoQhQ_mz2wwr}tub6a2g z*ObCu6U|0*8KJKW;y9ZAYZ$;vVtRyQz5$(0P<=W;8Ke}(z(79PO!>0T^Wh|+G;^g9 zqopvYNq%QO0u}?RI4{Hkb92YSe9T)`ndOT*Es?*Kh`e2S@=5XIRGz?Mx1LMhA+y>4ApYk zq=FLe0_-oT6oDmukp$2dNy27hSo%BjZLxD;+q->y;6?Lg1HEtdhyEXJZypcz*Y}T) zrDmke42lZFP)KEIu^VM9(I-)&vM;4o5m{#l85B(jCCZW(d&xdy$r2*6WgGk0w_z+} zX1-@W*S%cVec!+P`rP;L`~Ay9k7m5j`<(MSuh;9joE*k_)kgKE)Ek!y*Lvpa*S@>_ zis9?E8!a39W6E@8qQzx0M;YGBnH*wuIIDz`XR1R>Yz-NZ3KJ8QXLE((XZ@0ciwBpq zyg=S&PzJO^N6cG3adPTpg173xg9~FEvkN2qVlWCDQRxN*7{u#jca&uHAQ?RD`!LkZ zkAN6zo>C)b=p5;AD29>I9OYj86C*}=(#%W*_qjj;-W zxw-&E`Q@Ep#;t z)gPt)&fUfjY>u?uGc`Wd8x3ke#Cxshe#&)r{sJHbrxrFvNCW=1%UzEvOKSu_p5t z+2mtQvvP)_hHR~P5N=@syCIiCa(6(~ozDK((B6MLzD@vgWVO6kJPZc1Z5*d&;Km0D z*D~Z4LLWmakAc<+Y#(-3as-78O%9;{KKF&dYrtE{1J_cT4qwsdJ&HzDFa{Q)e9_g& zsc@q+uohABFZ+Rp*ouU6>ck6(VvsOVW{&TxMsDdL))s5vVd6x4f@Bd;EgGs5ZxD_@ z04fv%b>vxs6*nB+jpTCSluZl}?c~ji0ewk&dEIV-f4$B7CFrpwT*qtsEPJ6{6Y;mFEg6UJOE#P9(s-HX~j;vF2 zLs$v&<$K#&UL{YKN5MCpl9TE|pXB~Z0VtFVjMEio!=D+*97pd3RKl+gaOf6heaLuB zjn3EYlcU&ehYtYn0mXnGg}r5K$hwPB_s?M2FeOt!J)Ml65u2$Y4UtZs?1Q7DD0c)lX+MXY|y&RngIcd#8o2T7-D454^Mp8JlH$wB%UBb4e2~7ja{${QADj z^0J1TxnzvsRHvnl_!K_*{TOC-%G8h*Mz!RbS6o1|2Or}F%st5xI{_{v zhIfgOV%IM1P&Lo#(-K(}t6e1+-CvJJB*CitDaCA|rD%p}zIf{`tnC=wp>iBpYq_2k zA67>Shu^NJiLR-m`ES!!#Im&?K#i_G{W}cH?VK`KM8TQ0uDoFE|EVCb_nf#&z_0fhv7i7PE0?XD!}xQR|etkX6Iuot0F^HWdu_7(CsRe6J1u)q=J;5>m4<51gfF9r?3Ul9hdK>Y4P%(vU&e= z|Mqwy=))gWtV|l_6m$z}U&pGGz8)_&^nl4h#5&T3eD0U@$d8wwf074~o&(SK_z%@Z z4#NTv_8@*B9+?vNZyDuFTR^OSyX}C0jIEk=$||Vx4435f^K0ho=rP@%stqHs#6m8D z3bwwYXCG+?1Pa}^EijB18gdzY1TL`y(z-)n2ZSdW^>2~(A4w1Y`Jw-x2`XlhfuR}!jrK==k1*1} zZa-|m{TsAO(*`o)FQFxnB@ss0C%=BlSM!8Oj@CVmlp^#O^f*T9OPqpvt)EIij@|^a zx;VnwvG4~fp3@z!r@x`5!#mtpJL6#Iv6H1hSWqLebOZ?kX^k(^jGw*uX;OAvJ5FT~ z$p6J{3RE%CQE)I9tBf~opU1J3z%t6G}iRHV^u z7@1?2;A;F$8{LceuuB39Nw-7qM4Ckta99Jhpd_+~kfk@4%3+q*i34q2dKLIRxyg~;Hpt(zAks!^A(6x1 zypu52hT(Fxtb?Zox%YR)FA*g947bwo=p-c9Q=;(F4^4|Bb$Mt zE4C_+g)3>|w$@!wkDWn}Af<9dxezVuP`r0O=0LhpWx$u%W1)2jYkD2Z=5(!egm{4P&U9FnDzC)*5f? zucTb8Mmmb6$;J9Ix)DP)L$|q;rCqF(;|a4s>KA8})8Rouk4O?_K7=WBZ)=Q{Pl_1N zvb!A6MoMt|Vv~>h=fbG}7%P>M z$TG(h>b)$tAo1>GO2Zv&5?W;!(~O!z`53QojRH0Q!Jq15j6r`JjssZI6vk0#BYh9YUve z&0|-j(2_{op*>HR;S;8$t}j_Odlyo*krLtFdRXz0tFUe1-?m3{N?U6g!Okiu3Vem4 zGM>JR=8~er-l92}jv1+mdo(MXo#WL&t0Ff-8;oG*2{NB&3z`fx(S0Bj7+9BEPPH@V zHJCLl7HWg)aO#C>el#2TdlKvppV-gvlDmjgga9Kl@W;%o!2$VIc37l zEe{`SBY*awW}7%YYqQpJLi5)e0ZkBU!quV^W&5k&*Fb5h@0+7G9UHX>uvJb6^6b|E zuRf#U>Mb?6EwJZ!Z-_YMY_uiCZKI4<=oV=QDdOQx2aF}WaZmJ((b9ew{ZY_U8x2OiL(u@YP-i6gkhU}Fr{0Hj6r)loBJoaTwo z{DL*qYtV-(7iQw@I~?rk`r2kH$>^cU(L!fo#L}&>IR&aqTPPO~^ueKh$M2mVlLg93 zdB*gMt8U?1-DY&Te0DxegPA$`}*@@z6 zj8Dhz&;gQ`G?Qf2%TTmrL2ilL{nu!B32@+MeaDr8Es|zO-@j(lVwt=~C~xwRB#sci zHR=qJ8a9Qvo>5WgJMKF7ZHCnejKi(s*7)#_2IdfGguuz+Jx09dXhV0q9?x!@nkXiB zcfsn;(XF(sWO@%#vDX7NfzwU5XFcO;z0j}fjT={g!a%jEVt0lJ(WI9ZTaP8V{mL|j ziwvLQt>gevX6LZZ6ICAEYZ$uFDt3OW`MQpfzBMLMRlZf>i^97eGXczUXS*ZgCcW!_ zjBW<<3jT@ap^kH!gO@EhaGqhqulM`Qb+7m@0T$6!$?;GZTbYSaAKR^o(0l7n_55@Y zpPUgVCBKYp^X3&OpKReDfC&JIAmlI%*%O^q1T#fAFFgXps)(OEhPk-;ZD&DjJD-%W z15)B{Y2KnRA2z!`MBO^R!O2k3nhK!J^!!-Q^N{r_$a_fMverAuVs;OF2PB?PKuhru z=7cW=08zm1CaMnt5UW8* zKGf(o*7=lVedHF0;n$N31e!!au4jbPPl`arM37N6 zoWQP2rS<|;PuL545d8?wYIhSIN3DmZ#9Ep(^_tn@j z;?_muk$dmvR?s7)fF;m?(HhwEMo538&o);2x_-zzAR0T-iyU463a-gmS@WIM8fe2| ze1%vV?WoS;C#%{GKDf7|p=OuQHv-bk6JTqGr2zXgxRQjTJ=WwFLhlLK8bw8WvDffy z5qT;GnK_vPbGRq_9F9x6SIyU!%rtdYM#EJwT~rRz&?6+j5_{oXxE3-F9fL=d08@l5 zC*gcnl3ki%bd-CGVqK6lATA91a@T{SMfR*3n~+|}$$r&|32LpAxd{b2h4VtQ5pxI< z;?(qol+BAfw^2E^Udb?H?4=Rkv(9o`(r5+l9q2kFatgRF@&CGd_XE~wREI|~N3|Aq zCX0WjmSk_6WS3;h(R)8R2JEkle&g4_Gaqkq`haGIpsT0f7y$LFlW)3kZpZcI^jchm zS8V{63DsE)n&jph>biP&x6i!v5grBUO20JtC0<+X*go}}Mxt697Y|fZ3{dOm$JiUW zczxfE196OoKU68g8=rD=t;tueDsX?8`lTRtdF>bJA?O+Dc%+1CQuk6(jKo-&mo=2d zS|c0clN7%wEbriSDL-5@6q-dq1jH_K0rhuZ==XLLMe!*gj7Ic9;@6$7Q?atYPKLF1 zc3X?t68V`hr4hBwr3EF3zYr4`&^TyQ zM6I79B^0#OE*sER>OI3?g!Lb*iHv?X9PO3p4vFShR4BRvx!1F>^AbNJH(>4-QIL>JUu8QgUC}tdMYZI{AnMR;RFf^#*NUCTUEs@c}$$VY$z>dD~8H7>zy<1KMXg} z0&45w15^wnmPUlNK7lK44zqZ>!H9J>rK?YNdnS7<#t=SBj%+XuF`H(>13*`MQQsJo z{`!!n^I}XYy46wJtFs+&VrRPl=TJfd1{ACyfb@U>(tZ&zcmeC9*|lR=h;sRR6AWyd zFod+%$Xgjimg_Z#b9%LP(klFlDnjyMFmDR2%+M|;AXdJc;#6h{VtQCC`WYg80_696nzP~ShGQ={{sm^3fXx-7 zZY!s!Mxb+yH!STiH_Bj$3(YNw zLo7uT)UmqK?n4~L#yfC{PLVWoF$^O%m3}4$eW2Pz8Tb^ywS`x)!j9q`s8;SeB|l|Y z7b`=@(4HE2`u{|Uxn5~@0&u;+sc}w^-+}{&q+^YwRSr=3kQovnn*r#IDDid=2AjoU zmWX&^ze=gVk((#MsqvoTMrMM)BZf>Q%zpRzp@EPOCj{Ob+d5rL`1t~+*$iig!wh+) z>AR6Z9M{#w$W?$S0fwbMo@WpsoVA2A1`LyH;X!Yr9g8WdDm`yNx2+o1f0f3k8ktc2rodn2> zob0kNc%Ub>zKEb_tl}u-_gGl4b$9W(JXCQ@eloVe(dH{iY8QhUr=6FheK*>3658Q6 z@+C0pnBQ=0h_w0S#H+je{-7k-h9~r$lszqeI^%O|R?>A0AZXOwU9XOV1saN(g&TFL z$T|;g!!U1jnBrCojnq*`%eERDa>3ml06!|yp1`)hUhMxSSC66dbVKJe`@@h*dve3D z)?=Fuq+s}souCDu^znC_uqJO{jfjMV#To^VPq!uuU}hH^Xyu$Yq|a(o?=CSpN|IT= z{yN}NI(DCv9eXk?4rUu1wlm5XpdCg@O2fb9>hU>vYd3<{-zr>2VngDS`qig1CtKBl zvd{aA6aF)vAo<-hd`69K4JTCkP9(+|MQ15)ae5%&fV&C%RuX-`0!Wm@Y6ODRkvb(Q zPk;gBbcp~WAK_OQd_k0os$F)J&@`Wp^x1S72v4flSoE)A5@`T$rk+bKBfMYZn2|l! z0ELNk2I%drw5~oTd|R*C&`ZC9=6A+@^L_QQ5PyPb;r36=v9UW z0)k7ZNK!<%YR;`%VNDPp-=65czbz0Io&D%u6qPSou^8iy8Pl?WtY;3@F`rO*w*`^~ zj_!a&TH60Fx&fF!3+hWn9Gd$%fR!K2#RJnFmsoz>8Vd9DNs|9856BzLR#`j$Gy#j<_0rQ$)X)g7Au8a(m(gtxL$0h1JhY3g3CAsk zxrdy^;)0|fX)VL}8;F5(VUjVtGnLYJ7d*r$K z(f6yL65O!w(d~Ub`A%u#-B7ea6%+K_FLwx;ErNWN;J*}X&0wOYZiZZ)%)2I9?h;o z{+NgK6$*yZ)w z)yDzCpd-Pnx(JLjZT@7o-F8F`snT>^12)Fv4K*i%QCa-N1ks{4;iF%!S450=crb@G z>OB#|THR}KoPDrs!Q@D!euOR`4x^SSxb(t@wB0&%qf|I~~ zQpR*bw12GtV9Aa^#WFnxY*uC%)@QpFYNTy&>g%09+rN zli8VrSN0Z1&CLvU?aA)BZXI88^gdG$(z<~SIgKthYgwI)P{<+RE{AbkXNcD;d<2GZ zCBVl5;H)(Pa1sY{O=AHYh^Mhe#aehIQQ*{wD*3Xy9fw!&mDVVO6(iTNEpsTY7s!C}|L6B!N{K)-20wZv_MT)wXrOHvpV_IgzQ!RO~ zbyJ^;8xQ-sHIx5)iROP*rUGT^5OZn6=_GCba62P)%BC`1cI35gyYDrN+cka>wL0yL zxbK&MD&{PT>Z4rR=HN3d%C%h)O{+evC@fv6H>9U&yEtBJ6=DcG=j~v_HI!wA-WF&F z6M$I>L%ei6N+2G?2lOGek#n%Kpa@4hOC_9#s7ap`X6SR-Q)!&;PYislMS5GH0xv8_2(|=%qlcyZk9&@i;G$gchgCHK zEW0^Hw=7`bP_pmGOM6;Etb}!hq4J*$A!c7>?Yg2iZ^|pu_sk(A%x$k(UOHEH^F|`O zc6r`ijuzwdqEnP+F8nSto_ zQV-E znk@BVa5-Qk*|&Duh=2avGTN;HpIr?0w{+^bV1ReIGiYmrz(k;(nz!$D+H` zXP)hzg*xwMII{2b6vcM5hM!;i>UCgTA3X7fd-5`LFX4EYI8__DNEjk4CeA2Qk1-`B zovqF5r0ve`jd!P?$}hJvB^}O4DcE%MNeoCt)zSftn{X08YVvaEK}TP`iy*i z)UqSV)w20l&K2Xbn_e!fom-AWJn-OAGk_ zVS&F%5pt!|@DJI3`6;8F{~-PTH$d&*Q;59EKb2xH$~i~qeBQq(#aa|MKZ^}89m;3J z<{Z@(j~~0Me56-orfF-ec8fD^OB@0v93XL5ic$*Prq8CIJ|-NqV;Ty$ZcKX)a`v`}lV(Y#lLsrWg!D2er{@DosE zr2x%NS4j_Q=Hg+nIAzSg*Su!xk#5bD!~)ol7n=Nfho&=_QQ>t@r55rbi1m>`Pz#nT zMwE<@4o%!DRJ4UQSkAyT>!eIImpfw2qr}H+{o8U9GHnWDy*g(br$24f1^~6{-m1;U z$nu$6*@geE`~@@pRl7Q2J&jLvYD_vm)*98r`9gg|JAnoK52p`x_&X&wE6AekqFmmL1y*F9W&wbdz~+=S{mV`1p$Di_G%F zM_On$3%-^Kz?C$T=sr*#MROf?h@08VWQ|rqM`zwt1uNW;Fi;ntqa_L5k6KM7r?T7R z3Y!kieUEXZ^cLz!d~W|TkYXM=_yMp&-EMqncH@9hTqUME_o>M(XCK)eBuU>uWe}W( zDjVaG(tE&-lw3n(r=NnOkKVd;Z>rGx-S~5 zwTeRhCaOwf9K$HDZWo++umThUTJ3+Phx0;uVFFLJnE9x^VYmF=fd+Orfqf>tmUj=P@rTcq93s5tA9_@OU z(hQ_2n9Br`7f+By+URoip*bLCNqxS|4Jc48*2PlK&@v;TcK{>R$5XXFat?GvVzSn@ zCHT&1Mt5xinhH%a`nrox2=EV3NRLPwvV4J_3@qcWR52e=2veL%17*b7F?HUc=iHTv z`EA8H_i@WwCdS&;M*#ooQp<&wV=Y%&FrZDi>{zCv+3NsQJG(BP$XNXJy*St`ZBFCC zu;qUUfM6qg=&)_NTk|HP{$Y>*{TTlx2I}tSb|KS1;lrI1{gFeCxNvJPU>sfD*Po`& zOQ`HlYGbS3v|}TFcv~k#Y_NSUQB*fS*#r+<=o-aXenvSIIrrPDBW;t|$ z_oaZyiMDZxvp^qkrvmP?bStWyaF$iI4w}^!|G{-H$bj!SgRHo|kYvCtdi_t|Bn9!F zUM8>#F2wPssEx^a900x?LvKz63qx8wEjHhn17=C;c1(ze!%g9UhkdFVck{(*@-Hl# zfQ0`FdDZ##7Rkv@Iu_MwO#?QH4uy$%ZD3|r^k|A4_v zKG!v6uU&}`Zq9hdzMB*oZt+h7zAiU2P`8lc`5tJi;CmDvzU6?p*eY1yAGU13CeA+! zzsamBK7881CV2I&GHs-~ncY}BNx8{vBJX=Um>rK_ST6k2PRND#WdQRC?Ne@}A7dlajHJz^ zh(BemU2vmKee(1Y`-d&|pP4&(ZxB{MktGuQfj;BIp|syf1Ev9DRQ*iqulxl6vg!sK%41 z8A9s&;y~s=^1N0p{XeW>Dr$>*TE!~@aEDIlFi*5xik7m6 ziE{qiKl>rOgL?vEwO86k09u(tIrxJsuX&Vwt2^iU7fA{#*^i<3IVHCUqa@P1!TOl_ z(`)?G!srV(602R|1SxKi&8k+nFTF~H67SBSWl8%r^77E#!hL^bOkmhb=Mjsov~|?Im0$Is0eHUj9M&sdwR!6Q68j?8<@=y{yVu-`?)i8-}KWP z%H{Ma+fOGx#`_q4UsP0NT`K$AROtF3@)&kL_lmCU;`-a*{@D*vIoLYYrRZ% z?F61T1MvzqhoIA&O~EJUOb^Crb4N%N6c$>nz%qYLNzlwBe6hV*pWI6)4NzIkYIM<8%xmo8rX;$cs17WZqrg+H3WS8wtJ znYXf8lfj)1u!}o6t6N!58L?00e7Bv%mmBy5r@g*L9*~QlcJ_(9;u+FSDD0{mL?f=P zRUFCv>7Qj8Igy=Ce4qpi1F|k zw|*Fpep>1fM-C0Rbt+@j?%o1g%zwvg%4IyRj&TyT`J&AJ_~Brw^JXxa#{QQ!I0XR`6eYl`Y2Or9AP)bP)50tE7FBb z6L5m2#pDc1GAoDN&s^v3`MdM z+~Dbz}g% zyHk$H$GRDkKf6a)1Lh1VQPj0U`W9_;F%}=iYCpUNqZg)};+N|nX6F{JJ!IuE{H(}K zZ_HUn&B&QGmjrFjklhKnE6jxGB8qK$xa?U>xNJYA-OUu=Z8y&X7nLex>+ENf1}Tr$ z;!oFiFDeS>k4*1p)z_4vRAR#!28d*=26~PcKTmOu$`1Qr%g0GSiCND}@ zBnvyaAW7&e{_Mh{<{+GQxc9f9dL5PIjn$3e)9~kv4HNPCx%Jpmn_3WymK2Lq%mf0r z?Me9IeXKN^R`n1e@nBtq;1;`SpMp1IqHa%xs@&THZqnOQ@Y<+7n!7CKh^F(`Zhtd5 zzD@jiW55u3a)7quNhvubBdqp<|3wYohWDOYhXc$|6Oh@?7!b2YyIk)T$&UomeF zSSN@)^d~rd!_v9<5J?UEewzTkMyWit@CVnT)syOZw!_lcQ)5H;-omJ`49=u4Gp_FG zW&8J8&a>h^+VNztH@H$Lch_0MjvyznKdn6$*{_9X?JikQ6;bV(CHKtO*e%I>`9<7?95|ZS z&Y*NJ9Z~iV&RKWw3x4|0=a0rZavn8&KCQEe%2PwuOl9tMT3?gxy%hgX6Ni7?(0~56 z(|~t+dV0H4(RS+;PN4Rr*NH-!ZXW{g_XNbZGItGX5e(hwclDebZKGlaHc%HCOxZqq_coV^m;ysTg#MK@^m#pDk9NabBEW5=T}j7Wb%c`P!D{OCS~)GIC~PuOT7?F zQim8wUA#OM9Kf>J)0^S;R2{PC@se&4+mT`y@zgNC&$4Iogp5m6S zUI*EegT~J}a?e`4x=bZj4DZoF()9uoKrmAT-1p?!g1(<^oz!wphOY7M&n&0abti81$ zo)_uofHCaC{G^6e>|DAiiQW(Aby~WO4LG$baT>YmH}|8iT|4%ElAIzwYrhVT0WK$C zj&^j$jdfF%#SF%x;uw`S7a7I@k|pg7RH*5ACX{+r6{_2c8_gfuimol+K(;yHD;7JC z4f>8`UhP4MHp8&$kb|ECW&$)Eal5{3SWYucDL>}}<{bytHX}^)d)xeE4ryMuTY5VE zdKeXYp8YP~Ps{j@8#D6SaN+AoreYCu+~DxlI7X2r+j{}Id?c-(smh4#(VftIR?(r^ zwzP2I_KP3?o0oM6_UF-4B?m$gnn|cXWKpAEbkT2 zp1WJ+j`E)El2lCXfG!`ZU`vScD7gv*^4$BG^*vY^eAVIG_9g*r`K%MQzm9eVKI)lIo;ue0 zwnY?DCf$>w{MO4n-5{j*6tS_$v6<`V?MlRZVP3G4^N4b*%*?zlVbijnwTi)!i?d$B z@VAX}ABu`6`h06JcIlgjx&5PNH~nT%!hUv5;e%}VKD)bP&0I>*+VBMpN(Mvsww-8tLPPBQF%>CKqEPz1Yr9;n^LybMS3)3*hYTI$<>1d~=ivT2Xf z)FE^XJtMo#)1-{JQtgeePI;5NBk&&>1KmqM ziZC6cGeUOLxGNh0dV%+y%3*8S9Znu7w)&{mk7RaqaqDW*cSqBEym?e1vF$UYe_d=! zZxC{Hoenw=&&~Dh{Qn}4_%9k4KY75)@8OYiAsphA3vSn!mPYHZmmMl1FH4@E0(GDY zZ;Z)_BoqlN7*QQOr9!)w;2tK4{pf0)V!S>3#_naBsQivp`*Vz7?<7NAA zGrvC+^Zg_eYT2>HUq+_h5gC}k=gEQ{{w=NOZQ3YE;`9s;9k57$t#$8SN=RStiXQ*m z+$n?c-rk#MazA|%`rV;+(fQ7uY$H5gDEfmAo#?qgD{U_3_JOh$OYIfKEa{A!h8HUw z=HxB>@kLN;M%keGuwltcs7<2JnNFj_ExYFQ8yo6 z+$h;XG6+e@4B6y+aC!;v0x2P~2)~_&+o#Vg==Bkcl5HnzI(7vK?FjN;K*VzslGajh2PIr5?~tBd8xs$B;7@C5H;qWv-YoReEdI1PBsv}lN(P-bp`leJ zdDe!73P0wzU&TkEZC!Vxg=nY|Y0~z1Le_GCrR-Uqd)8mrAaFr*6V)nwh7%SyG_19Llk`;33p&;jE zLvpHMP%>=i#L`$ENufBDE8nlX7@fmUNQ$G0l6rP%31`9?XC@D|>Yh?Zt@As(iLiY5 z_C)Kl*EI+zA6$L=%L+fb(J$7jiJg%8?H(l}dyBXhspXDMfd3#swEg9yd zzW6hHwAQ{{_C`5ZeuB!xPf`efjG_cT9idgJ{<>>r`sN+O-eG{LXQ+aMCu|(Qw0*%k99qGZW zoH6uz@PM$->kseW1ula4p-L`dq=@1kb;oZo;#lrSbH9)e1mEr)8wFpJ_@ldjf44rg z3~1E5`6L9@CPs>4l$J$1ZJ8!zv>(>*aaQEKT>lyU+##Hw08WQy(3QFFE+B8n_&~R| z$R2$;{d4ueYqO6nbm_448(~*Fkj2>b%S_0R-xwT}uy8t0zW8=< ztX+M%Hvs_5n+4spqTPk{S#X z(7*Cwb*8!Ofi=_>!MTP~CX{)mm|5SpxymwLzt?0d|0bi*VvP1FS!AQ%!v5kJ> z#FWOB?(>RMPoF+Tzk*MaZc$ra3r5vH&}vnuit(j*PH=ZVw2L^C=_%ei{LDh^*3Ad6 z|H$~F)BbzGNJ`s!61o}UcYaR!kX^va7Sk*`bUWIU^^>G)eqD0Lr^ok{o%V}+bX~tv zd129E3a+f1Li3gAN*T;q7?|1R_08?NQlY|F_66I=Sw+$GFJ-&q7ZdVJ;Ash~I_b#h zX~^E!yySj0jR}vA(%%)-_pdy6UTbEgJ9WvB74!FFlJ9jCx7n^pXB6GNZ>M%8Dhk4k z{+P0)OSx{)`mLP$^KR44pHa-=h>w^ovLQq0-F?UUU{lXRov>vsDo{WtMQVR$Z`a0` z6Aa%uZE8LH3>S1)QDc?3u9;T*MGt3&g-+R7m7+`;TmCpL2Mih2V%gfNYE`Wh<-n|F zd9x^FJo)|n{e`F1%Nt8QM_M~W0<`%>pj@+>x0BKqQ#3q39(Qhxl}9_gd))SNO|L0l zF@EJz5qz-lR(weQem*g(U}wvyv-r<5E1DmLCPSY(S+Ry|!b}8Pp&rzhIHiRRIn~4D zTF?^4+P~>T(ekfi%Bd*&g8lD+ls&6h{gz~x4pt7;+R&+@zQU@AC1$Z}E6lcE8;^{_ zwZQ4|n^niohj06|PEIa>D2Q$wD!KKpS#gt)}>m0-Lx#J0C=)*`X{i+%v7s znrn;B=_u450R>Q9gN{_w$-MoPb9oqnE5sL z;O!%-&|W`z{@m8j2oH$x*bgp{$I@Os(!f`D?E)QG;WU9j_evEp54YFLDjJ}9aKHrU7bhO3p@+A41 zjqmJuuo!U@LkE?qnUTdM4Z8Tr7M8|H(V!S402{IYnt!UXzT$1Mn&nqK_M$3#?D$~z z-Elu(41NjP=*dQb3YhEqqEAfyAB#^KZh!x4!P_2o@$jDoson(uEF4V%_0(b7`lwtd z<;M`ziQTlZq@x0<)4KNbuET=;lnzzOqYsXgMBEy#HdeVdWE0RO2=Di3W-k1g`<-SppHX>}=)&;D?P-xGq8m^rRanLEhsHes}8Q+m`S?t}x^2^RPHqWFY6oVE9E3UTJM~N#e1E)j5l83n}`?BrG zw$(p=G{WjdYTjmzn=E{}HPM`yiU&f#@Q};rxnGN{$jOGKIHekE8k`|h#?}s~WtiW@ zjD-=0!0pb@4-at%A80n|zykw^NQt@30C3Z6>Nh~2A4OM!duovMvjffYcKk zwfC5T%p6gd8j3^0C1QhZRi}PfVzlQ-f{fPQOlYvt zED8_d`h4gE^jJdC+}z9Djc2`8dB{!ujZps9IDZ=Th;X9ul$OcC2LVPJ2fr(A*R5M{ z)yzmpSQs*5oH)V0m9zDR?8VwPc6@hqh||vMx6horgsKOezpZ=zTKI~Co=6qggckat zxXFX@h~~@S{?-hXjosF|V`zZpE`{!e>jj6r>mxOJPUmLp?Y(?KT%)zOH}*_UcEukb zd&s7Vm#iLo&7Qxut1Ct!zP#m@@lUhb>yYLml(4a;LkP1i8Ygpx)}nKD?3q*}Z^HL! z?5NHkIJ-?>zqL7`xTpJ163SUm-Gmc8*ET+;=g2yY3u~|+rQQE>u}d>0{P0vAfBy)d z4YktcN2EgfGsb+G=~&1aA#C#f&AsT)_r(qC6zVia=KT_5-$lApGfFPk+_JTjHe;;G zADgkTw2>eF9&?RwTRm&3vvZf=xw6%YK2}qw9wK)ec@GHtgyq9COu5+hIw}TBgE}S+zl6rY$txR@`gLWSFnHGet33YwC_n&$JG( z6GzXMRi{(fcV`~eLquoqWLlK+l=Xz$nZ^#RdreZWS1gY5JvX9`BgXUO!k9YWG64Nh~R2O_BSF<^u5^%vrpb6i{}Y5HLR3 zwuyVPrjljnkX^?*cTv-ORyhFv+4;L*5Mj+@Xj@B&pMqufl>?XeMo<09G!flOYjp8n z?u#rH9qhJ?aza zJ=ne8n!f@MCsCrTee3$zehb>a6_;)6@q^M21u%ymwCBc;<$1UN@mCuu=MV9^lViMh z519_@U(HKFa(Y7DZkC{+YUP=;1R>M+UWXWDb?CV|r~lBs&p{xt^e70(5`QQ|b=fVK zt<-MwSX;>t_h#t8V%;`y7?7~5T@%wC9ZLN36F*NM2l=@6TR{n`X644q0kIMPP*Fpr z6IEi6UsRXH*`G$vgj3s@CSL}t%-C;~nQsE0XuLTfcx66nZoka<+C$0_mDlEVGGefX?H*R> z%d%!}zO+~RCpENwn$*XcO#)M{nD-g2m?Q4h-@?%jN}oPns<@ugu6Uh1e#DKqLX@r# zKdV{y8EZWg)6l|ymd~)px01MYQ0J_>WUQiR419_j%(du|!bp-?%J3pB2K3(0V8uem zXLah!zxWoaR7=UH?EeK=hiJ19hexO32dfHibsG!s^am~t@r!^?ui(D5zJxA8wkPM| z!rdiAdTD@Abn?xQ_(_M!G+ctXWsbG{rjmHIYeSIX4r!Df38nGiy-XF1kY?Y#D}5$f zv6wt@BA2-MWN%~7xA>>bWxlXcL=r}+D$q$Id#8l?W$JiT3QLz|^I#B7^n7}=9$3Q0 zhxA2HZYtlRALf%4yI@!NdClsQxVhTT=Pi?3GEXY9DS5U{nptyx-DQIZhI@N`i7T?n zoq1R<+sdG2`;ck6{_T3}alHIX+y$NS$7k4YO36A0Dil2!ut>hc?R+ZgIIKlooI z$7LyH-}T!#z=zUEr^PZfR=TcMNr5{+pNH z-gKGNeY8~d&4+x>U}Tohu^2N;;a)h<_p6ul^1o*GO1$wEVXe(K{NtMnW?RB6Di5|K zQ{rG(Pw^lftQnF!|GL-KG4<;bHew}bJhzk2R{uS8v*4N<*u(lH*nUHs!d z|JkM8o!1=-)ySXgrcyr_wRr6tZWOVDWL`c~#C!;)BVE)~t-p>X zN+f+ao2_?OJ7N-ZdySnzvya<9(Y-WxI`&?+;H*WYPoGRnDO2FFU+iKpTI1R$Ns+Y$ zNxJ7~7;HAA%Yl>L`qflAjs(wKd1V*aT|9b!O?t)2Dr58*MXb-mP-+>cZ%Y+>mdntvEUdsX`;-ycqSDV5aq>HkHa|37d1Ja&A_)L`B7(<0?? zpS)6W69D05^KWja*QL~hlK@=rUJ{bb`Z*~bBhBu&xfah_y8znz1G=#Q0=64gY}1f% z8TMblZY;U^vnswjH}DskU!i?^&4GUV@YCL9&JW;#Ue3L0sXNC^(J%N93`)$oNJf zE@_3zB-V>_KnMTLpJs!)QKqVK?^3DKm$%<=!rSt_`BL!=K9}u3$`VP>{fzI zEby=RXM0FrgIz&$w||G8mCSq%sI9X#@kk~ReXN-Ty-Id)uM^oX`3>$0;xTuS1!ioJ zC3P$=c6&+<9IE0mWEO)AP+q`)()OwLysFkTrs#Ihx&G5|XqVNuNLr&ha_itqVoH>xIj0gxuAfp;aq2r^rePqtW__hVM=%3 zU`ftm37NrI6ZCzUxR2W(j?#JL^4wAR%5>SnVq#)zcptTq*89PbO{_t1?RUDq&tN(2 z-9q+#2Zj1K*f(R<8S2}nu;TDg`LT^h9Qq$`XxZnh@gJ{`@YH!i6kF#@IWB_0Yh}g9 zgi!sh8eH0WE3{gRT<5*Jt^?Q%U46XOiY>|0&}8}ZZK`zX6D-!kLM*RV?e7EB%|V*_ z293=j{Yc$%P)nfKklk}iT~~w<1*dey$+dk&;k zwPDxbn$KM+cCE{> zDO$m#(DS=v)pd>AJP$iWrgq7i1x0dzu?s4?+wks$`g=vEcP1=qywsibKa%->1p=1= zPl`AqsX4#E9eyN3sLQ`gd0FBX>%|rQZ`!=4Hq3tL?%GM3F8Nm2_Fi+mYf}D0?gCi$ zO_uUNuk&!xczP?$l`x^oEu?6;YvM*syFT5U;l9tmYm8k7Bc4xuM_?Bt_%k?PioB1C z+xU3%*ZRS?@2`*N&N_g?2x>ge5`R)nmB6{_3 zUM*acX_jACdClWgZ(nnMWgG87d0Bjp(V|p~%NobuTo|%QI@K5Hc(TU!Rz}VDzr~jY z9U8l9J6u3+L#zEybyTGVOYm5VT;~ze85htt#W4z^&v*aUD%*nPR@Te}_Lcz80$b}` z^{S#^YZ<^03AaXkx)O`dy2}UpBflZw(!!skgFp5x4az-smmh>^q9mPb*Pv&W2ub}! zy|bHpuSL<1k`OsYFH z#6-_dVt+*uA*zk_X*!q!5<|Z>$xUR+6_@bByBH~+XGZn5+i}j!Hr{M~rFmK`(!duD z1HL1${|meObV?01$8IwuGlOdPWjhGxB{er}VJV%?SR33sNmiDYkT;?NgG$>O+y6Y+ z?aWd|*VssrOnQloRiG~6QhpcfMD^C#SraMBo^Ka>2MP$DR!^8b@f=Kl(t*HpLs~#1 z$cK}yk$kZDLSce}8t#Hmaf8yShe~?Cm_2{q7_(|kee@{4Ss%EY4%%f%;QBDxm7vJOIE8%kL-|8~0rUak6gIU&< zMGb&AWR=!`+t)}R)qZ!|E+b9jnhEuh6Z$wXGockuDqZKQ4DtwvoKma=u~xh~Q( zQ&5G&Deq1&WPp0-Z-pE@)dkNE+PeghZ-)%ykHhm*H2K*GPj9TnxfpYx%84ag3|HX4D2dq0!ifUZ5U2Y}m}zCtzOI)}0UDF>(o(+v`bzm8 zsw#Ha%4diYz-(;_odM#i37#fSnE5;YtWx`@9v42^gTKNi;Ix(!={#Cl56#F;<9$c* zmA;v4rrc?=4*6{{b_q+Mmb}Ojmk@oAD;etfYRQmCg+0(8-D&9L%tp84i2;VO$zo1OHgI_hs{ZpBxNTEhI3RY@y#^uE_!GG4*4mhLLY z>FnoiZzu!38tTXV=5(3{)X;F<{Tv`xg1Y<3Eq;l$^)}z*Tdzk&mu6ce>`sLnX+#Rd z?v4A!pALdvX}rcl7Ul|>vL@lTFGNZ5uzMpCPn=r}SeaxV>>3_mVcVBtkeAGXPpHY# zD}|UvdMp|}FKH8#`KN3UEy2rHma3eP$Ns*lH-pmIxF*=)UCMB9m>J#qCTqRNiQPVt z-IizM0pk*sHC%QOIOJ)jfrgpjpuAaD$ew?YV`5Z}=#DqXsD77EiNm)J-2|!)kyKZ1qRYXCbr{_wbw%gFcy`r#|SW^72*M;-_`?x>f+= zQ1Dfo{A}dL?}Rvt;1Lf-LwEkn^3;1Y{mWA-Zwr3hqhk&`9yyk`La8c$sZPpsW&{-q z>zgzrSz5~GaRzracHMQ*OIFocyFsOesq?axnpxI;Iw=H`)9#Rly)nIEYaRmmjtei# zxo1htvVrfg7u_sZg3))E_A7Vm{W6ya3p`RQ7dzamtnr%vVYHXY@pL=E7AcLd&2F00=*FCf`(QUN&;dC|u$WkK;7vpI! zVg&PU#hj#^6#kp<0l}t<%T`Umh)Eua#i%>m&z-+n4JVpAsV1pftOZckdEq~Gw)J7= z!+(dZ3ifW?T7oUWnJdb%-KZtN%pULJQ9f#Qt7t8ztvk%QxQQMMOd}|+YQ|~LQ;&ow zUZQ6vGwt^LHV9ri=hh4Q7Z!b4MeG-Xi%6?wk5sd~-Qed93+UDE_lX|D843Y9*jCN) z_2ryYzm(l}K#AFRR$qH>>Q1fmH3fzW2h_Uo+pTQRI|tzw^2b~Il_oi!sa#e8km*jb zSAMjY@Ro1{efZgg^3Ne7@m($Lu!D|B=w=F!8iUa zPizv_Q)~w;Nb7W-7X4F<0^S-EQG#H(1jmBZ>kuU>L^-P2L&%o5FrzEO@;(W&%~M*K zx$4pNv5rw&WJKRxsZw@oanY)qdVAttq@vcY7)J%nzO0H#T68%%=m-f6_@1ry_ryeq zvCG(KK{>;kEF3uEb9tI2o@4aE)vr4qe_z9+WySReBhGX7(_^bJISu1~-9?VzP1Bf9 zwHl^BXFnbuS97w0ubfTPrfoa+t3S2!s>Fy#Ey8dHZgVZ8=LCHR~1w&TGogPFyTj_ei_hq&om6NYG9FbaZ zc9%m?PMz5?Y?QDF`&eoK03(I;az zBRyvD_o^eZ2eFmu$bFePbbXYt_V?^Anb+pZ5)rCmV_#!J(pX)24n-I!Exa92jp;|;YiHP0u33`+4lV3+NYYCG`+Ga^H6oH_AY zp0Y>HB}9Qw80i_qwi`MmJXQA2RaDr%Lg6a6R3*y>JQhRl zv(Nn800LEFW%p`r7ke&o>tzL!3@pHvj4yss-F|ixHV;ykH{1y05s{@;d8Jw%7R=an zNgpVIbSg$L_@V81=)*Gm|9UKkz6Xxwb0<4sXN~{${u}_BDgDNw-L8GxPH-g(YM^tR zj`#xwKm#T5Q^Uz!!k?)(wva9z*`Kd;lN3x>%#J zs7QJKDn6}FANH|~$oyw8=M3D&xVp{sYDggKd_T+$bTnp0d*ixOo`%lhI|IxfouT-a zi2xsR3|VFXeLet|-}8KY4asmt(s%nACB>6}J*D$2GWh6D9#5xsrBt#MPg#Yq?V;Fv&YCCi>bb)gWUnU%KEVJv;LwIO^6FxHKJpk2s^e-AP|$ZVf@TtdJ6(+lx7(VqixnbG(Xcebh=SXC0i z*V>&w2yMVK8JO#vdEaH9Z8Z9vVYM~6wl_bLUx@ZS$CgM!t*{!d|8XORzRDe@QDa~I zc<@&XW!}+wMM^^{KJUo=tU&(e*OL>qhSoaw`2}q&m78TqH*PK^zK$#Ju>Y0l!rYFY zfnyF-+QE3~)K4hdlL+Fd4%_XdrM+}%E=6QO%1q1%cy>EaAEh0;Kp5U^n&{Dpf(AEA zQ(NTm*2dYoslwc2Y2N!B9L27>COm?H0$pllZ$pKCqp`upavruV9u!`$a_8~W(5 z=s_T4|8v{u<7V7W1`(^S7J(B`r+bw}dt(?+d0N@v$I9O_3C`Phtu zPNG&4^iq_UjmttOLE88AB;I0sL6J>031|tVfiZh(T3D9oovrz2S}N;er*$cOpiBNb ziVGhJ$W4ek^DP;A%G@G{T#&Cl9~Y&1ZIgfZ7}0C;vo31zW|hy1dPI&_8gVQ8*9szf*q}}9B^BL8R`O8gv=R6c9j>qIQ*=c0h^zAG3 zJ^1s5ok}(Ix|&rf=($2F%B!+|qVM5cquWuv7prqGTdoSN^?49{I$7fs!yy>>nC{x`ZO!G_#Sl|2tF;6X=xTkZBXKJ zkS^_$8jZ-bZrH|BBK9VK<#JuK1gqj&fi_Y8@WyR)N@1@*U0@g;YS4@+!9!{x7L z%zaXH6Gcu%Pvn@1FY(UTgS_VxI=tP7zE3f7iaGti$I(?ik$WZGfw&2(j-$BlkKRMxkpuQ z;uK$gSiEVx!gx<_lPmDp{|S~?dXMi120#Fx`21*N7xKQ{Xy+kVH4`ev?c@?(mTK(G zeXuymENiQy73_uyC>jz?Th_3#gY^_vOW>`f!&l^Ezh<5l#pMr%dq%}qqsNtEjJiKw zi)Gl+zue+*!;P0b$q1SRsusco9?M&b(v@*{${VMzYNsX|mlsV69A`8#9>Z70!_AHE zB`}%`+n;H9_xf~z%;LVrNhbn?L>Zp7y7i)_Z@Hc1^k&Nc$^v*a1+$t@(ny99z78_F z8YA2D9D~-q{y@JzKB&C$X8Nwf=T8d8Yl(E^@8>8z$L9|(e`lDScXHK@mj*Q$Q$JGW zwS7i>^7ugoW49!LLR!Y7Wt@unnw>_c4)?_OH*sx_W#S>0(wKm@7)nlvJ!W{Qkkjj? zQ{CegK9G7IIku-Y$!sh_gOAmAWFL|009)t;0nJMjQV)`(wOSE#Z86*vse>NDJ8R>m z`Umqrm%h0b$8C_%yUH;)P1x^!gQMLOy2bgB`nWHIfGPRxJ2(-rpwkDW^u^1{bx#H; z!#14$K*VR*fcz@5wpdngMQH5GX4pcF_FRpRuZ;lAa#*cJi_V zJa=u9I^&CbwD7C%*R3e zY-_y1C+$S$zi24vJ^`bZHqnYr8ITX_tr<42_y^NUX!}TnZxel19T{&w*0Ulh_5H6i zcoR)+GOB~3Q1EeQ_!1hn91;rd&#<&fC$$zoF4s=CJ%#1C&|+PJL*knU)g_Dy-FYbN8Nw&&57U7XwB-M|J7xg0A$IV*3zjymv~79yL)VC+c~Tm zx^0eq*s?|u^iQn7)GYu%e4qLpk2%F!)`%{Va|7*%-fj%=U0ut zPwr3cZ};o#D2AQ8&fT79g9BD;95}abMd3#D!^>%q8ft**_#3mWX@!kJeT-%UlEt%Y z!-0F$^zC%GUD0Po!H*T*eR-#mE9M`XDV^U%1dWXP zFMnmKVKUav%Rf#A{Sr%llntU0Pk(4aw#2=Ns?Tqcv5sL5IbV%C8Ls)jgV1+2rhP!` zs$hoswW0yGsnOF$$i3KAn&7f+38Gf%$9Maif(zl7W^*)sICkepz3?{xvt0^ zVV@oPYNGM3jF2WyEvhuDLrMh{orr(+Gk7ZCHQ(u9&VWfgJ!JMgCnxaCN5Hy(*W?K{ z99sDhN?Q>K?0!IcnEe5$CbAr5kprq@VFQI+A(WFZo_)W-o=8(-yp;*pB)|%0J>J{h zr~>B9tg%D&l%Q6f>xTM%AdJ%~2UoPCtHQ`)Doo9s^6q~y%CxHX(t7Z6sPMEFbB}vV zq18o$=}6i@&>VD7!bTsRS*3e{2KjP4gNXlw@tMp1NgzgI|M-jI*1bMGK`nxav1uF|(E>$Sf* zx)&=LdpWVNVVR_xH-@y6D6X47O||*_G+S<{PA4tTy!omTUnOOYF>*}Bgp2F}Cxltt zBX+5c2bl>HS#ebw`N$=FwK|YETX#13>m!C&&1@?7#P0qHVRItX_u-m7Ax)JVo~c)7 z`&;3%AS)lIr8(c*Q7ib&C`LdYU2dNq@AhZDqfq8)dlvUXY+>J4a%-}8&!yk3l9i(m zwZ(Zp+6AYlW)_;2-aSxtxjn2kq~4bdox+NaSJ-ukL)i05AW0yguR&4bt%omAhR*%| zPKQlhZWQ{I)Q8OR5&mf@uAfD9;nk(5!LtjOTEi=xNw8i!MsAToHH@rl6=nP3Elae= zW*6CgB0^C9(N@LxCEt|B!3YcTr3egSJ}lj9Hp2O=V)ntAm=U7}8IX?saI?(nLW2{m zWU{Tf8Z#5A$i;Ts-p#G-`TkxvE)3Z$X?^KihYaV>)zp4%A~29Q)U>GlnHK=bzaG7k z?_9I)8#ChPVOVzsr?=FSHTqk8^xx6eV6|V^ z3~Jb_N(#TTY^wcenc0&aC-%wnLIFp&kZl@m(RdsxT{l!y^Jx1gAqw}|6YREr$;#U9 zFrikR6vy0gh>>X>8&mwueLxNb2k+0aMg=1Y)sxp<_gom?U|o7L&P?{A^f8|=5VXpY zL99m_ik+sfN{Qb;AtJST;F}OeVDMPh8*0f5IemiA$dG$(jQ14boK#hFQTp_1OnlH! zLXq$NO*F}GLUOZl<$mxhQV$)pQ_p@Se|d0%OK>Q_drxb;PP$kl!T?Dv`Q_4DS=sgV znVg=INk<(JP^Es^s0yv~nLI z@BKXv;bN=LJ8k$(J*;|Pm6XO9EQNCG+%R2aMbfh!C7Rev$D({9GC*T^6!!TTYO+{e z@1y&-w$+CYbG9h&9jb;{nupK&wbbFVrz_|yLq)8;BO{`|(x7kMqRIn$qNU`%_4UpJ zm2}v4^fnIz$q#a_USl_Gnsa<%D*bBp>!?<*u$OZ`-9R>vfH6(o<>e;8a*7`H>w9v# z{yup#wi_=!n7(qIP%YOgDYYts9nNRmsK`$hE2G5X4rq+HCLyVCH?WrK{d&-dwEh~Z z%}fyYW8qfs;`-FYn(kRi%}}^)d%xh>fq5q>6&RQFPUWK@K^PV&AYA65GVU?COn^oc>g) z&tSRurMR>E`&%UNh1E49L0?jQks%EFCiQ(OWG7C#)DA1H3$?6#7j>S^u9Y6V zR}NuBA?FU88x%=MqOzE-a*vuJWa&G0+f{4h3wo3~6$l1|QvOpdG%a|~Gp4prVSlC( z8sA6IKv5SoiYusXHm{a;3ySB7f8oo&O@T2P==#w9*hY^5K0!O4jg>mD2B2M;gbKmA z3)i-Q9L#s_^FUR}>2@^xe$q2Vwl}-@WcLZ>`Y~nS?dKBgpL#O{((G(O_5#VpQ#1&3 z6S4)J<=#06-OPe*^@AI}v#L%C_2j^IE**+0&fHOzG+l#5TnvU|RNmDKO`6lV;z&-&}+f1XqbiLQ+|FM`d>p znn{Vp05d%+2P(3v)-(2Ymaz|BFQcYrmYd+%eWaXvUc0Vp7Oabahn5ba!MhJ9r_cSC zM+HAkQhJ>)x~BS>5I1syAQ;Y0%@Cz#Y$sJ1_yVCV|G!Y-U*K^2%W)UI>>gycjpflp z*E+=_0U(XM1Wri(769q-Dd;35S}Rc1454;)@}jNAj=;>v{<_$z`(Jl{|~-;1T6EH zKLe_d<%__+Nn^MJc|aDtJVF38>afMLMXdOD#8pKNJlCm#lw!a;z7y_cV+UyScPnb3 zzknDU={-bri3fj8iQ2-=Soem-cg-2VwF-`26=3}P*pik1GsUAI1Yjm_6?Yo+>mfvi zd;zix4lc|Jy$p2)t;!e)#q8S>>ehOr8XvNQBe9ej{!t#X=iB zlw*plz=qhMeX6YBuiO0-+3Z(suK==eT&7)Rm}2N@{PEg?Sz!rg+v(S>%a0v4a^G;? z=h<>3(GOnp<-Hr8ynvBj?s=-(f=1}D7Xgilt^s~Vo+R6BWd zl=-yvUd7X9CoT5L9j@kMY)O$pvgRA_hKIas7s<^FAHroJmdKc$>T+$&NRey!+dl$o zi9)}C;}W^6)KcuAL$L+fWaDYyD(0%Bn8tjp7k63#r$q$xa^2Ez=jf--7@z7kMC125 z>A9~~Ih;y7mvgO}1mn2FjsAoy>BVx1w2QW0+rlxaI72C$Q`k4}ss3lOx=*Z$2#ED! zSx&LBbu@D+Y%~PC!2^ipY;o*W+iOaG6MwiT@K&4t2CPq0EbGd;Vfh32y(2d`Uz9!l z9qXzvrOr(D!q1*r%NYNO_9IJ#vy{|Y@!vjRoL~x@10~YP7_7x^$ed2Ma>eTp8~Z+F z_3q>>SAw;}wgoL$mY69}HvH+BY3Rgi|Ff`gQ%61h{-`M4xq{mOQ zU5+1C$*`$>y8mM41v5U%Uf}O|)qP}wg^jJ*{)DiVKJ^LhEQD@h;2`u=AmakLa)i?# z=-DYAV2|Tm%f<|Js-!~}sfZZ_`UzeJUgFkaXLZCm$tHO?UPHBsXZ>q9O%}cusrXc! z>+M&Y3hVr_pGJaGx15KlM@9H`oyMZt^qHRP2JA1_A<6g4<%fP7|FG&{$I!rXD+l91 zIL=9D@4&WRk2v*jQWRd+DfRUO4%Uma+J0FEs+N6`0jJo0{f;a>Auu_|2P&WxAhfge! z;J*#Ah>pLwq0J1&Dzs0+D)B?{$i@5u)8cc~gSvyHw)~dU!WLYMRWzUhN~=MlVsbB~ zZqOs6=*OKI#P>jeos%Qo%fp_iq(q+y?l6tgoob||oydre%klsTJSylqDs6>;NT03w zZY6jCLGK|PdjoFOLc2eH-6daYO!yN(xmbX{EW>pTr}Rj09$LF?*bZ?nHd^Al*k}K_ zj{o3Ip9hZGh3@R{IL1%R_DvNpFkL#>E_H|s<)TkGy16sE>nBv%Js2-sySOBAzIxk; zj*l|S0vwGOM-=XmW=~RH*!T(BnPtg=^netyyTc|M`4_TpH*(^M4&#K)Pj2ff4pV$L zUwoeev0fyYQ)C29caBL^oNV(-)B2LiyI{~FlgtC@ywWX=$?W?bJ7CIoTR_nk^hwqu zG~TZS|41}(q4x1)2hmh};Crv!J!>HBP58pg@2Ih1*)^Kw{A9%<&VG5C8_%`E?PjD^Go2;$8kFtV6{PDfM{CF?4pizAVTT%zTd;qjT zL|^?MWlHOT;3o<2`)%efa~pL|p?-sb->uc}paLZq*JAgj-L&Rpc$a}V^uMAzK&|*0 z>WCd}pM%U#_;YBs6s!GmXE@I;RB63#gndo1$n`XLUh#4Dd6OU;$n4n<{HXN{#b`=_ zdVO`;;@-p4Crv_XZ#>-s3|so09ptMS(<#HC2?}Xfhu6( zNpGc#Z4iIbR?9qUpN@Y??YhtbNX|ng-37%{eZIX+gcbXXlf%Hfd$e##3N?0fceb)}zE%>Qb$)ANH#@~Cc02bV%n&Ly^?|g9HJK(74;o>iJ}qPw8Za!!2`58W z1OG(blwWjHE1K{`9eB6Mct?KiCq_}p#E_2Z1%_~5aRsOUPukW#mQtt8I`vp$dg=~} z>-memT_F|HFZ(vq(elmji-e>v1deK+dLnB}*rXtLbwx1D6S)|?z>b-)kt1xa>aF`+ z)yAPUmdvaEmn;7NPGbDO|MpH85PH57esrF3m>EF)DggS8D{4UJUx;+@bqkC zuD$wis7_qzUu)jEhlhK=sMi3ZN^Y$)=JIzB3uNDSS;h8CFdDAf00D-HaNvZsLE_u8 zKjx?l!7_F4yu1Yv#OZFlj>xNkb}$YQ@v8s^XZteWi%O{ms-ppo>JN1JYoD9ucgR>M zFh*Z6w%|5TRr|7Sgz<+oNhlb#!IjUQte4Nq8k1~xk{PP&tIJ#dCUKpw74OkmeGl3S zGAqB^AU%*3d1V&FsonnA!?>dyeop0~tHRgmOuMVzOAZe1KAua45?%}GV#0_it=4iR zJ}FgOLawFHq4S>egk>|T^AY3i+R0(e!x^_k-IBV}qz@WO?EQ{Vb`Cw|wHpTY1}#i( z1!~OqDCiCbsN;&w1XwN&(OHvuK6-5Abadbrp%`hs?Nm7py$4L%yTJmtUgc`?Jy*EW z-}Bi9^gc#(B}t4Yvi|j4@ab4mGq24VpWbHPu>DOzTC*ptu-FI+ zWq&*h~7cbBjZMNYYyR z=vS+Pd0pJC2YndEUnW2q@nc#dsKKg$GRBHG>NIp=LH}|A`Oj(Jmd6$zY3OA+rl`3_ zgn)$WOeTjgZ@Cu>dm^v@TB3lG$-I(q!t3#)smd;#YEX)(*NYTa1ey*8$;C~O7l?Vfq&*#c{L zcutyczC8l(FTQizXDNJvAS2ZzCdSu$eUkp;>b_UAd^q9=6=Hyw~ z4tG9XOe{`z=HTlTXdLdEf1$sP*+a`m7S-!adIs#b@(zGc8qm|>5lYvNDrYIlo{9;7 zMl}^(Ybi40wXHm_WZc<0(H<_ex0Qtvweg3@M{XK_;$vSOgMd541(nBuSyi8o#Xy^Eno-DSh3+fyG3^J<`cJN3%c`Uyoy&H$J*-$zS7u` zBx5IfkeS!09tki+a|eO+d#)ufc2@hvY5>0+I~LsQQ)?eGQSn~H5u17e8CdjASS8e$hCKJ9%M9r}1eQ z?i#mWJ3b9l9@h$`O$^>N;Md+8Z_Kp;GJTP**q_)eJG z1r065F}OZ|(XJSuJojr#tR7ts#`?JeP3J5ggJ(tqcRo16?nxOa!<LjqVE)qEVS!?-<>GU>93Zvh&r<{8K~wy;WFvxIRBy-LgL%C zhLdUmcAA_eb#^QfqP6TBU8X_8cPZhDt%fQ+0_bg|^}7CY4Wh=Y%pLezi;Pi$d|%7` zR9v^f6X~x%)=iPNL1|vgcO}5siyuM+%6xArknSDsq;;NFs9>ijQ;R_T(h0qOD5FO8zH?E2UWBMr7Y+TP1NVUr8`gP|{2LVDoj^ zJ*l6|9>U~Wf3J8LV9o+wuQA^90svgKiECY|Y9re|z>X?XjB^Ii-gpc0)?+Q-eZczs zqkO4K8SxgNJZ*gNO<)gtugGhfz{3RNPcP-FioWSLl)l}0AK^B#|J1r?VnTakKJZ9< zprA^*?Tn%b#rO%*~FgrmsVKZ=96&;hB(~2VJv9e zpLK)s)e1K4ibTOuxqA%UL9u%Pynmv4uxPT#He)4b|H6m~XKlPgpv1pky}iwIZKy1MsSsLG7VoTn|_}&i%W}?TX654NV>cS_yN$cJOpG-<{6TvM!n|raw z_!$L0K|U4r%7w(skwZp)BGx|FNPyusC%o1kkAsC=>y%Fl<`e2i$OkBTR(|=4vGcym z1MC~$MNTWMw;o%C-@%?nyL_ezPT%(SE1eZsEWPQidex*TgmL%sIO%av47I!e;2U(* zq3^%hUiAcy?pZB0ExmEr`Tp}OhASow&tg%iI?FUx03K>&D%M)=N!IxwSXY3FKX@0%eG;b4Fcw%EZ(jD7;o_0 z)JPctCW*j**H>YGy?5kV#MG~oOM-&zVtG~C$$n1sQ1qn~XydI%s$xqDxvxgDb=gkv zrWL3UEhp|%>s8FN0Fvyet|+CmXrPn#k)uB9ZnBDOq|%q`MQfUh*qZ#xA5qZX5JwA9 zb5b3XC5Gfn^tz&LZ^I9&qb7K1D<)dHmbO*RxyDEz&3c^RgP5o(6Pq~n&$lRVy!PNk zPL-|rqY++|e3MwF%kSqgc7o)GHQExt7nz}FFNmf>uj6FR`K|Z*_9C43qce$8tl=yh z*(np39}q9zvx}2eS6Rj1#@D0FF*89S>Flgsh*s|#12TpWQeakn)F*yeJAc~pFjHp+ z@hkA>ZDGI4o=Bls-|)LQXd)q1S^hJye{GO4iEd+wS}7ie;>peAKRGz5tCBd=%NO~G zEfGeSIky_6O^m*J%fNq4&&8cBCNZbjW8WRj?geF>@$A?a)m4?AIUQL|)mAXiAPWiz z`OVzXYOqcj^KLLRN$91#-46-x_DX_#uJ=QiW*2WxXEG)8rCGCPY4b1eE)z3+ED-Mv zmfpXmM#JKK@sFSwtr;6P%FBC^5akD=HXn-~4CyF%Nt|ZAIN!?CaR%Tl8QwiIAXkW4 zWtNpO!utH5^e&ZfeLHP1ci?UYxiu6P=@KGub!D1g&Kbw zS!M$t2r&|LiHq)U{?_Lo)`3f{{_G-PUFnl2l_ovc9@RVoG)+MDAhIKwM-L`5XbHfTG=y*y`4BNAas_uWfy{ z(EC;W+sCp5=g$(oeLjli^)_N0b3IpsfT2g9m-W#veEhu{FBLjH8y)=!AohkJ|rX zwar(%aGZ5~0#$Wj3kTL02dsXbO4(&V(c#?=pSjIv_wdAV(rI&xf)xq&idn5;7}g^4 z&4cgNQh4<_?kyVDv$5&!Wj@OhULMjULHjpoufb5YZ7L4N08`Khi=bWoX0_c$X~VI}(|yiEf4!@fn#!1|iUzyy#-PtqWi7T#Q$|1t zP|aVNTlJvhYNDL}eRh(Dw);-H^4fjfR?6z@BZaPuryLF{?dxxHgISO7gw<+3 z0Vn3V!*$NEUZf2W$l496yrl6=vJdmb3vfj)t2L524LP{e zu&UN39s>l+)R6^I5H+SXlQtaoPnRCR%sAI`s#8}d|G+PjYIm(A*t2i6B%p3k>bGw? z_KEf~1N;u&nLiL#hC*mP*g2cDV$4n%PAkc^Cqqbdue`xsUhwYY8#D52-l~MmGSMOR+IMenXsf;+aq>Dy^)6- zyo^bp%TT%G!h|0hP?~)xlvX(STgONVLbTY~+@(sI7R6%_zh)T%oaJ2?(w+hPI;O!k z;$x$pEzRCWot~RBvegPdC_Uo?)Dni+tMF^ITrae4iSk*V)mHKBR-mo$txTSjAG>H$ z>dY*tqe*fAOYpzWiPGT_g z=ZEV&?c^T6NxC&h9etd1j-&q?8O=Oka5LXQk(Z(m0QcL2hB2_2IK#s@KJsS|;jopS zyVDo{j3C5;nG=DWrq0Fbht8p;UFS48;9G4X!WmNAxJ~96qa0mFBxofnQ5?Erl;A z>S9OaJ!dAF=FSZr>=1bBZet;)FlTWSzj~+(zcO?>4aapOJkEQgz-gFxoewn2PN{8f zUiXSL#=>?XPQ8r5niDpcs0cGDURK*q1j^~vs~7P*{=B4K+ccFtEt{j82i1rz-L*PD zQse=NRHZW$=T{MeoR@^2{*%^AJh_pES$`4&z5urao$+3M^4|!hv%&t%YQtlBsz>ppOem?_|n7eH6uPe}p5DY6f^snBHmfGLfAU&8ISE1BaT zcr?;W1s(%wy@WQKVqA{22Y%%J?RO$Pk;;dZngk#kOU~>Ac9T+vk7KHY>yR~k2pH7Q zA^s!A`@H-i z=;w)@c_tw4MxW15iVnntOmrf`_`}g|-k)Y~TmYejF^HO|cn*svNF)_(0S? zCzUKtdAEh`7-W0Z6w79z-FF01iQCsdz~%Cnt}A}KS9zKQy(G!?l2SDfJCw(YIH7|k zNg$Z?rSAERvT&YB@T?H>61n>mF(R@tP)PiErvkbfs=7?5Lqd zHy2UBCryGVKD;Y2PC1b;BnP%t@9jmV9~kzP>Ld|_d>8M8*wi6X+d?9Lf-Z~jfl@x+ z*QCuST3wC}+(YIuFHH79eaOuiy!`_dARV5m03`fcn7sxC8-Mo1Lbs#-r$e_M{e-qm zZbx1a3*9!pd>tWxu6X6E)iB!<#hd&)7bT#W?m65etvgd{%HeM9a(j`8c1NcpmVAEKc1l%F+-4Vmn;pkG|-yeBJC;J>Q=QxbIm(Qk%a>9%*udt|a$qqeQLUN1!TtvRh z%NNOtY@Ir0ca(hW@(C^l**e$xW)Br3Cq}+2u5SX*a4`6@gU6il+4!t<-13`s9q{6> zw4||FK}v_es<~Tz-k)dg`BJ^=-yRbt>Zj#s<%#45zw)rjp8V9a<@|I0TnKZqLOwJT zaNB1s)GcMFh{BYe-fFi_-OS9m-(V@bIYs-86jxp6D==gJN`*F&n>|+}p^M}!A1=*U zJ)jlln9l{VRF`ip>j4w&WA(?IY)GKJ|8wBl2cA0$08-CN(bjT4`I%VG0b_G z*_hes`}BGLe*eGz=@Pa*H)ZaR+x>RC-kwjEkXF7(-!-OV>O2xEo=ikPhF<9$)<8&o z!}IrmKI4Vt#|`3*Of^zZrl_4p9RVTv^Ure@dfC z%jR~4L5s3CnkmmGVc?jxlt-GkJVmPmmy-21_&l(;Z@(+_aiiZ#<^yxI=xhCT0*gI< zDp8Ktm?OY<{I&^WcaeA80`4pa43FJSztx}%D;N*_T1aw!^|RxOXgF^jqt}vf)1YYo zTyIGT@rIyjY8d5lm~(dR($1QA53~4jwnnmJAobw~C%a<2Ja^i+m~wO_bzIAb#)mM>0GYpE<#* zs6pG;PZhc%zm*Iuzdh4rspwE|W|O55aYqVb4_3K7)vl3IIUL^xFaLQy7-mxE4<7!d zRcG59-TJ^Jy-E?f!JYdB58lwaY`+YEZnN&3AHbn3Pi8(9Y_yZ2y{WhH)_!Dqc!F)* zc0G}%R%>0YI8^P^mt8?YHn1$LL}4zC-RnmSo}S7wi>_*z&D$7UmTPOr2ROF+H-y!Q z1wVxN4CMKJ1W*}EuPZje=f4jgXOIhX9|-*v&Ysg$<}6v6@}xy|TbIh?y0-Y*r5hXW z3yGH@(Bl!?iujqQXRuN(U=SXk85Kh#@4laeZQpGOIIFcLk}lm>`z_GnZmpNhydzw;rppwFVuLatMy^llYP{W-Gips@WOhL!$4 zck$;`msMzCf5~O1+xe4(}V8KJNmh(U)P0 za|iX$5(gf?goTJ>ea#t^7It2v{eD&K#WdnW1bNjGaep4LrlR5)^0Z|NlEnZLnukht z!6Xay#Z$(tkwT_a8Z^)l7NQeloCJ@)u65(qHgBZ+VUZmLOl$K$;oNCzaGpogrV1N(G&s>m&$@4c9 zX^)her|;Q$HQioFlQz|F3#ZnhBP`nfD((IV-FXtf%n(9<(_)!C;8IY#(^_%VF;S9q zx#CF|Cyjzi-)_nGaVr=^^e(Fl(SyZ4Y4_QU=O65J({>*G#!4sRL?{q2%Q-f>$7{Mi zAgyp$iDwiQS`UC6hoXteq0Y*Hd4kXm&A6qL=>31YTNY60eJ+h+4QELcvyYoq2F;Yw zjKSteKmdGohQQ?`-YI6AqUcY^Op#aKQp9zsMY9w8Tv<)rl;dE=45gW3Tvt&8J^WqA z59aK|!7f2jSr;?7m+fkc2)68JyrFn&sk+1gK;O54;p?stfz|5cQ3H2%Bbh^|x_AMn z@Jq;(9H)wQ;j7QyRK$e9U#qUbYpF##LF-d>=Hy?`#+NJJ2h=;|>bS<<$?touwL)HQ zoPWSoQ#ID%4}0rNc=+Uj=sUCn9up=rnHB#ygzcl)p+tG`X-ZTGhhXjn1NQNF#vM+xr^%dZ(|x;f7{ zj<VfP2>&~*(y=-b{s}#>?@2@D}i;-MBMhBXg zYaiQ|OU|u>DNY5GW$RIk#AO4dcCIMEM>duUTp~yQhzXhvaFGQ#ir%oI!0L8@yQf?q zX-8E6>jW7OSD zN!x6oH=xVV8;t?9{xHJE6eTb%T?-vnzx&1OcK#PiQ@~on%+?7IU8#dsA_QVu6}5W5 zrg(Cj{e`)1d2mEBjcx_O9SE@6mMmgVaOYqLH>e-t_$ujpk6xx~G^O z$4Rm?x+g%nv8qOQRuh#oJLaDZf~;*NK6>_&guK?&&#g+hrS)EB(=*q4ZziC_KHEf@ zxgcM)H)`2^zXa`;nzY*gRo|q3e{Ot|a`Ln~d&^V!mJrtqr3!l>=M~2WMZZ}9pK#d@ zUU)giAMoeS!@lgxH%^om!1o^$3$+QSQZad$>5rUTg<-q4L?j*ObUFvIaH_g7?2Qme zJrZ?d3MCqpcdGvc6_c3jJNi6P0KCOq^2$6H8Li)q5(~56wyoPXPlakg5{cb<^MZ(TlX zZLO~a>JrftUsOT5&|5$!BU(=^VB7{8M~C+dW)sj${ke9 zz4qCbeikNjApD#tfQa$#jk0|~><+bz0K_;FRfFELSSsw5Ccbv}ZZ)a0x!Hm)U+BxL zZR4c|BY)MCrdN)IveTbdH_2=+KE0hRrfq^DS>`D6YyS$YJR8`!=$Q-)T~gwonfzd; z6);^npRD=1IC#KeQK4Cz*TefplZcBWuTtIsF7Y=LRrBOcp@(n(YWS=S-HS1&<9miW z`&aVDFuLyEmV2PY2qeC?DrE(iQssEw>YbgxL)~^-O=rH@n#NS4>9W&+(qCy&KY_)7 zS|%H!S8ty?(kF%OAC{NO9SNfj{g%FvZT&8-9^D4|s#I=fv=aC1_dKfBLTb#Mp~X*dNpwk>(KC{@cy*9lIG;36w}31D?fbykdKPk^!8YL@M3QO zcXY^-Lj1JKMSCR5btc$nE?V{l-q504=)MT>FHYGK_@d|{rul5(TH0HpXcs7W)XX8s zG1Ov$QJk!EfA|FC6Y1zu)H4&BDlYp>eVRr=ne$H}kY5$V1xzPtBi?@Ld#7zGg}1mn zeaxrE!oSmh<~w#v2&9tCmF1&s0|tHM(@vr9k>xMP&-QS9LJu}TK^A$JtscZGfxb6O zeQ%8jYpbk*{{7iuXsQE^e&$e3I|44F&1$zTF2T zWpU9SosH65e@I)smhZa($|XCp_U>QFfI?7BiWew*Wt{1jzg9J|6MH<>H`ygN;sL7a z#0zvh$?{a)deU9}szC1DtngXW`2n;ns>qzluNG-oMuqaor!gdPSqzBhJT;9{D#xyy zO_xzC4K8Hc3PRHI-55ZC&tzebh`Z6Yems4wyt?^3b48T9IT+b|U^L5PT;X=O>lE>G zuf3&1E$aPI8JX-|uqE_Q+bKhql2eOl`+L%Wre!ai)3%FP`)DWgbgB6Edq`E&?)@H4 z8Ab1t=mJjaH~1WH`e|A){V^*^bw$YcGGJMWC&i^Y^Hg}w^0PWKVe>AlF28z+C^}DOR8Wt>dXzDA0C^Sy7`@* zYJ|hWOF>7uG(r$mrmfF_<`jsHPS!A?#6zo;6y-h*Zv!0dpKc*Uz@efKQ3BdLE9}W& zwHNH97~%^UqdQn!o7(>MRr7V0lBYA60SuStjh#P8vSbSeDMHzTs*6a0wR#hp9H-u$ z8FU%vPO1GcqmV^HNFCdGU_Y2*nzcq-IN;C3bG9obeu45UTMjy9;7!4xp#~`7|LlOD zp+DdoPn`rPbm~4c8U8|j0G$=WR&zEqJEXrpIhPnU<3UenJp|7CHzIMs?0efsg~$1a z)?$Wr2Sh6y(x;hy!iD=G{adv=tTmj?Y5zF8&l)K;CA`Bc)XKBV+A5WDk}I$M1Jv`V zht^k%?~v=fHx;{h)wY^WH{N|d$T)@HbFr2&;)6~UUba+yyRr&!p>&4Es|XYzW4n^w zeHc3;yqAw^LbZdky;8!wN*r9$2X_8nw$57*CyIRY0TZ5`X$ZH@jW}x4U+Uhky0SkEs!E&=GDPsdsEeXI}vi?&()j z0_z8P2i5uxc>hLQo)}jLKjw(!JM)1@O$B5>r5Y2;ocwYBL~9zCwpuvT_^#33^l`!0 z&z?-mrLkk4#+9F|3EtIOQ-1QQul^B7ga7?~mjDo|kOU{@Hwy_BU03`ch2O6NCXT=+u-9`J~)X{4KPp4fxB9{YNuX41MNug+p5zFXvSsTB1u`wwSZ5WAP;H zX8ye{rSaBUe$!N>Sor$pcy#gszPE(haXYj|TgdIja)WE~%~!MchLw)ZSrv{QYpDp_ z;AL67sO5v5Mwnxu%?lzA`@W&XE-(X~I8b^s`)Nt&B) zcJ~3LN$bJ*dpBucQs~1cq0Eqttdt14X4KZR>%3;OIsNiDUQd?pz*$sL%Za|lvmhp# zMNb(=EBW;2s2E~IpXW=|wPp*4I^ElTvB%nQLRh%fxMqaXEu)}8iB;1 zGO3oNqfP&^i0CBL#-f7wOxao%m)(3Iy-^a*Q{|(gV+K!@B?}Q5zDKeDN;&wU;)4UJl7=Q}yI1o$p-+ay zxGy>)9F|*^P>)!??fa#bxl#-#wXPRuP}vy)iOsW?P5j9{r-KwkFTde(^)JTK1KU~* zf-e^1j*I*bSb;}xH~WxWw`FmQLvx0&g$;%_6X#p#@r!HHdA@b(1$3S9_xl_X5o4$s z*=x3*zk=?zTO(t$H`A3LPoqlk(ZW2v=irywi`LlJ(!1-aYg+J#rZq2e*wdP~f7HZ+ z2rB%f9Ki8yTiyE2)k6C=#zF9cxOBzoq|ofa=Vw6SkjNq+BR^Cv0|o3@h=@SQL~)tZT7Zy z#77ie`wuyccq|Ap*vxv{EM?`Q;Udg*7dI-O{y;i6E~pH`2CYvd`y~4KprVoKN1>%9C{I3B zM)vceZI@q0&qnrJJCsBBg5!~o)7Ow=e{Qo#txGFH^~eHsztWDUTT`;lL%fr#DxXl8 z+A`DCVElk*dLQ6@6_nsD7?<;1kaIv>9j`ob>dqIoS&`wIpaR){lM;XaYo@?PDQ>A>MO|U#@#(x?#N4i0n<;##-xZU$wI%xzj-!1@IiIF zGkV+*=sCDs6>^_aZfJIgp@CYRf?c8~xs7Rm(5L$=! zEdI6Qb@-~O6F;Jhfu;9jUkU30$NHwb7=V-MAg)cj4$g8OEM|_TgaUb23a`S}EF*7! zON?8EkFh_WpuzK(W%4joY~9i*rQ^*}j*H;p&k*9u@>uy@)3-Gk_{-fbM$|67BSNrt zE>>7d$1J@9w_-aUA;mC=V9}5)yWwa>cerQv;LD58fpei5?!}!Hfz9m8H>N_s8v8 zB|9OOPR}(`m`n;%P_au0#}#cpZwhx-2sMeT)QGbt%j2+S0?Nwt&CA>4Du(~ zB_@=r1Zj;6Y(2R*O@QB>Plo{k;%!Bi1QR;j+QMNigL~^^)+99~uH`%#?UG0A4ucTN zN7I?&V)zRrHq|}86rUTgVfd0N7BP6?U~hY_a#8alRmsbRiPPj*Z6)@ppfkvg%V`C{ z(4N-rqpor!!Psl#iu+N76A;>8loSiG7l$}$tl2R{isZ`NThGc-OoU-dNYaUwqXN;o zlWdZ6g)F*xX8Aw^Jd13Sk?%`wh;QeAo$7QC)s@tt*|N7nd|n5U&_%sMj!(*nWS* z>(v5nX;*KDwrcB2pG4Whg}uEh`jQNptqvF^iqE_WDq?nh)Dm>Wjtru z;1d=8&2OC^9~}h_uaw{I!RV+Sd#}0O>Z?b$^gQmZ(p#Q!M214AxPc9=4(IXygRv?s zfRjUFg%21i*)O_-R=7p`KsQTc5zeopHNY4sLhWmO!@Zu+{%a8K>%dodOQMbk14Elw zI6JQj=y3czAa=cDb0!!8vs#eE@7l-dDV?o0Dxf~S?du)3@2&!h*#tjO#R0YO4 zwH95r_VYJ8B407i@wcA^9-?emilEt;rT?)jGAbB+bJo47CCMdG2=v9}H}J=MK2jFt zX;&uj*K4W&cd2TgogMkmG2kV<0G@%He)fMxrK=qVW>B>}4$ZehAfBsjdY4&p>@6mD zbJ?#gXx`*#>AKVOI1}<+eeW96iaXL=s7d}U*%tLT{iV`{7`8XjQdz9UOA$AA=)hUm z+2Ov>9XXb4GFxiyrwjccuphSqwD#x6%xe#5JWbaq{i>P9u1jVQufA5rN11x|PS*y; z{K$%a2|<#vhc>DK-ZL&e9JhUEg7Px)ie1z0+?fJ)wg^_CvppVM~E8>82r|$==4J z=xE{wg&@Ito`)DQ~&bs=)Fl!Oh%!^5N5ch z?fUNG6rKAEu^Y*c+m%39u0DZ#6!?_J1T_#bV;`yZ6hG}p?dFU8Wy`It+Q=pyG2raP z9lkI!GcAt*&aSXiA-iD~r+du%s+*VgsRX74eI)tckuEtfDBW0FHw%AD@0_QjMOb2V z23l=0b1eEJETHd9oQSe*@0|Nq9AU!8o4Qzdl9k$i#a=`^rNouorhOC8(2r0%=Y^;O zrG=Y`#(oK4ms>iPUFbe1Cn(=@8D=rD+1 zD_K*-h>$zIJ%EyW{*EYT?xjJbi0;D~DlRjZP^X@kOBX^dBou6u-s^P4eLLJ`G;GC^ zGuFw_3p2R_D#_2O*oCN)odAaCts`KA`*s=XoCd%5HMArD4VB?O|GbSl&dJjg7Z|ku zB#W+4x4@q&q{;@a{t-=G`VHFzKPNGzmFrzV*_H$_$cHs&`MbAJq=+@JfsPNWaRo>0 zNB)~Hoq=+uigD;-j8UUE=$8K4lGkuuNdi@1*>0<<`XcP|d>pI#LP>4ah-qu4RpN{$BB1vI7i4Rw3Zn%9WFg9qxlVCEE8x3rr9 zg&t=j_TvSmR%!}wi{#?{b$)HwaJyFpkx^_0i4%9QiaXhew9R&dlo6j!TK}&+u@yJ_uCvmi z7dNsED7QIU%B+9FMqE`*qlYx>+{e)CBe5S(S7+FLy4(ECU|vOGNwh{612ot1<#exV zJ$o|B@C0Cc@J7?Md)!UQh)Qli?|6DYK&NE7`RZ8ZK7W(-#Cw_`*yL)3Eu_V@>+eX1 zIlLmMB&c8EWG9}_{C)+Sc&!XRZzd?zeP=#Ms`%GpJyruhdb~4YZ%*aL)vIMp#wr>q zyUFJl&{TGsU7RNCeH~WAd@qPMT)iRibo${f3dS#Y6t*GvU^3`Ym<#+};NUm@f{CZ+ zxFc7xc5-*LAMBHb(mbb<10-@F5CjVrpBv}oL@$?KMf3vDXB=ZN#V}l|FASOtXq#lz zL7>oB`T6AKRN$vKaSEzPWKzDkevownljI61LThZ2r(-Rk0XWOXJ1D9C=EEHFyQ@%3 zCCkU0!Qwf{a6;5ve5jQrSx7LXr7`r+rQb%UCQ(5gGjXbQ5mL1`6{ziM`r7imOG-0g z2+q1oVLbxI3haje-rjRocK^90%0>4V~Npv6e zBF=ZF{gKrnasduyNAK1nQJB`_3=W0S%OSAvNp3j?$a@Mi`j!IMppfeo8k>GBayc{i(RisbuRRajf9W1fYmoUJmUFa;mP zwFDGN>Y_%zy4?3|3Pa#L-YMnpYu-$`QLMl6=%&yeOS-@&4Fvr6kt9W9*;?`mb^$w+ zI`h(vg!4iahZnml$@j7hPmDxd-T&Z{(VV5?NPv63v`O_DPy=SUs#7e!26BYwz4SwA zPbUceWEiQmI-h}vD}uf9_^%GDs3?h(J7L4Rq~GMub2pCzJF<5|06>hP^5~nIk@C(uI8{LYXUDlgwoicQ7x4KV zkhQ|6GkPK-hc1pw{%`fB)T;6HVEOhJ0d;D7$l@eqI{}Z%so$z7c$eL1ONqTjZIFCH zDDK37_?c*erWifHqwonwk$JV*RR07&mHtvQ@pkaqzC5)l&>x9 z!)E91E_iB06fnpXbln8zp@+2#l%xvlHDN}_$wPV#Ur)ujZUN$;A(stsO%CdcY*+gK zMytcXb(vSr<2A57z#}=by{1^HfYuS1?CSrToD~NiSKP|Z_TXFh2MS1QzZhauo7sII zzO?eTTM_r(XYAD)$H6wA$#OV_p4?l$%ZfhiV;vB|;P-L;T)STRd|ronxT)-SS%{|X z%Wm=F*N`3$P~ze!^aZo5`+H2@QntTpvXm8O?4R_=#Lbn zJ6AX}ZEuR)crEXDjN4gwJTWu$q9eVr+f;!OWh7P?bN2GQ1?rR~Mr3r}nMc(a;?u#$ z|B~GwSe}xPQ|EShU-;B)77DKM?gebh%tx1+rIi4<0=|$%Sp~rmYy>6V{@Z*&s%)X} z>u$GO#L$IQ!;Wc;mc5iJX()xEvNEr;nJj0vi{ zP~@Tuj)b0WU~ZXWlbO#zX{I{rAvWFNYlOO?u|de{j{z^w546rTaH|CkuTxJPO2JCiN^uyj$4PMA(m!`Irml&9yE|&dhVFUNaZ1oR?x+ zBcop>BDUvb!q@x&d%ej?V!WVq4s0}iR|LK{m*zipwKI1c=rLE|oAAjFz>BB_1Z|}G zV-n$v6B7x!xtnFGYEaKdFwXZwL5X%e4{l)-6q#Qaj<*X^J8;RZa$>_nV$r#7-=`#( zCyzusw>5wZcGIK^hPJ)2@QF*?%RVSk+3mRBpB?R*_MiJyLzEn(%=7|uiVNDUC3dvl zegER*Jg?!~5z6%r>hhtxCRx)+is8Q(ykqF~NwZ!WnCdTS2C$;vw}lO88n)SDYpNJ4 zcUh&O`?mZ$TJt$Y&G^ZxooFgiQV@CfgR)pXlOZ@iQ)_(Ui89x;*G*&&mhqGe_4NUc zXWFu56uKTsjH0it8OGC2{bmki`@E=iA(wmEFs>uqMODi5*Fr$58`oh_$g8X@KAbG1 zdL8y+KmXZhr;5n;gHzSg0W2=-Hk{>*qHRlLP|M{p^g?dm7r0wQ*#0*U=@sk3dXt5( zO8}*{dGg;!JX@k7FKo9cCPl200p0j&JRkf13Ws%->wWN;Z&6GQ)BNlebIhkgczt(l zZB_gHO2{5baidFiu9?mGx9f^2Q2W-U6dJ_|Ir+Bw{zY5*9PjUV5lyQcz9HD^nK9SL zD5Kme>sYScD;ElEtsWHUX+BP;~}Tx z;ZN7M#~CrZ<-jZTM*;jnpp(8p(Uu@A(k|Th1`$4cC*FkS=NbSx_&s=o#f}{QUWGNW2ys^- z#Be(<9+!c$5TJAzziY0t-bn*CD1*Oi+of=l7cmrGpbIK!RY!4d$)imvli*K72Nht% zZOw&L92Ob?N8BC!SLY^qug_4>3m7hs+Opryq?AqER{Rjhqj?=gDCtT6Xq-@JE8#n0 z;PA;HN$noyU*dr4uJDGar(;)u)(1+LxsCQ!*RIqwPr!Tu3ZL3wuL@@&AHg>nN${kz zoPE5;_8rl?S?`h=Vt6RcWc&DqP@u|||1i64Hk)`NVqFEf^-|GDW9E-@Z>(|r_MLjd zS*Uv@fE<0%!pr41Vy-$ZH3!XS0PKwN+8)l>3*awduxGnWSNko8_&@|944dw~HifC! zH`59eON3=dK7#E?h*6^y%^d9YNq}L^=EHX+a^*upN59UB6v{qj*A;SN%|Fa%YRr7t#(z40BP8=aFU%SiVWS<7ro9Hregs6! zp2I_GLlo%;%9kT_H&u~D1pIRu-YmvzN-e;vFx>-cuh>)ja;pLB`+V%RiIaZB^{gI#v}9X)WU7(4G0?BIY)8$)Vn6JIW=UI-u3ef;)IQ&%0rDwTD!~tJw_4 z0rNf!h-=>Mjgt2qzc?M#)*Za^KF&mq5WSK_0m2~jmdN%z6Ik2HcrkI&L^zbkL?$d4 z6N<0oH-XJ9g~K{) zHMB*|Igi*xJ|HuFeBB3Gqh%qeS=+LEFEknTZt~GwxHGAM1`yj#9r4dAUS_(?Nf5E$Tt9V} zSMBE+8Ja;qJ-OCHW|4oIvTq@_uHUNe&;2=* z*q-Rh9TXGmHbpvj9ZLgd;MUs324SSBnT9=97=l<=>cQPrTy4&v{vmKeXxVG4Wm5t9 zj~;!_+H2?kBACPbBl;L1k&;zB|GOS`4A8?q+rZyihWt+tdzK*tdOP8C+O(6c{xf$1 zqxB8sYq<6E0TqhaYew^a>*?(o2^I)w99i?1(dORvrCWn&^{N5_CN*1t?SAWd5ja+ zj&M2_Ifipa0^{YuWOx-AxP#=N3y?-jzps$?Y&pgQ;4&>Uz+@UB4l9yizB+rC zuSYN1R!~n;|M1>w@tScs_f8)OdT5qETIr$+d&}b&c|ssZ8MSOS=Xe2m360Q;CRn}! zor7mLjcWj&kpcJR<5&Mvkzf=NAJ3Qg+M(EvUd@-Of5#@$c=GX?8zLY5G?DIs;b(A>K8T_j8de& zGDss6A|vGGRACV2N26zwviL=(Z`Z$nz*O4p$l!{3 zG|lMPU_GM7?vIf9Qs(N$eU_k8xD}mH$2ymaS;q|jmDuz|eV+J>k@EBu`BYe2mcQ$x zqRcpA%|VnF2npsm1)SZXVC}6z(X=PKdFm2CKlaHwvUJlBqZmQt^08)RxSI5+$+Yg5 z{E8zMKD${e>>c}6?w@PZ42{shVw$I}-0*zX^>w|w-NRIkv*A{}%Y1Bz6yFH-V!`%c zxV)maE{Cq?I}YrxtDQI&oW?SFyuAKJKpmshdU@ccf$&xLxctv*UG}U%bpx|~r6H|b zPdBz+$zDOmxITNmF`8Q&_W82Z%ovQSnCjK^G0~WG=c$_Nc1rEK78hY{?Tl^VF!Q7# zy4VIVKb+g}XjF2lRfjBD^|nT=m3^9*PJ_1!h`d=qYXFBWQeL=AOl15UZ?Nuw`m%`p z3#v(oRT@=KTfO$M%5k$p3xD>uNYaI7_h~L!u7?&{Wcm3BnYUtV#9TUAL7$ z&}km}Rd$~^WS_=~DtLMpC}y&4y*e|7p1n+(<>(=l-LQPzY?nd(Qf8dcfF$##3YMXey7K;qqg0_s$?FFoGq>rn{WMeb394xBqGI+FnUsm#rRBcc zP0;z2`)JwCM(cS1lK(naRTF4s<>EZ%)x2>v9@Y3twM-|5>NEN|T8h5?M-j-V$P3(y z$Y0B}XOsM#%nN=eq?lH_%*HOhc}PeJer!HW3_AUtCB3Hl>F6X0yT&a!?6%F#L8fM6BJRyumw0%zf zvTSF_okebsdy4${T|?m?#7wNxj3*O#HJT=dWYxZgOP)IG-gsMu_dEJn?e8mWWy{Hc z#&)8xVkhQX$ps;ichQz1-`ruo};!h=lai-TNR9n$>fDe5u*kJRn% zuk(Tvkg%3t_*k@^%!zjft@l35%mAt&kEdFfUU^LhBB+quyZGQ1@y$G(V)`ER>XoaW zCn;_yP;K*#9PBTz31ZsFb1!a#!ha(cZ}!0YS33L)bA3b0;1fr=JhY1$iFRBSEs_95 z5cuKtwG3K=@8oA?yjN(pTHfg)4ZPiIFGZiSB`k&A%Zxq7E*=cLpD3EyS^{l=hoyl9 zxp{Vx+4;Jds@z1>uBZl9bMmKa?pgnWwu^tX0FOfM*?_!<5B8^B*8eKq66211Iw3!h z<+OK#_zBL`xp{~dauW%E@p}6~o;H4*MGwoJswNfacokH&L*=8M+EAezH|w8V^^#=dzoT^hL!4Sf59uqGQ;`&v_WYXiZJD-e+@%fd_)U8W#Ac zt6NSQF{IM3g#&6ujZAQ)X5w=o?ND8E=0{dw5~Mm*q)Q!qBFAGzU5fOEaH0<~V>y^2 zn^%O_rrige$e=Po0%0^Iq6B~x(*Ald04jB6ATi>oma~q2%Amp$9#iKhEoxRwh#-V@ zCc*bG%Q~{2GpOpirL#Wm;=MGkowqG;G3XX!Ua+1}zxdWpYFspECFs{dBx2qmMP<{O zP6PNGLzY$AG(&>KZ_#9-0>b2q*Q6z@b#$-a3vRvrF#jBke3`^fw{>cXicn(o6OQ+3 z?&tW_0>^ZDLOs4pAim&XUIa10=@hFSLm9nL-FOQ#>Ll_|>yhAw2eW3t8VQg;417_A z{(ZF1A9_$J%b#Qm2$mXaLVifet$crc=??!QU`Reg{RG1*rV+%l8u8Qfjw|Cm*bSrj zTq<=aWraD-7uHqkCZ#q$Q$?$ncO^%}5RMsBn$E9+zfpCH%k~TCo`=|-os3wBARq-%^K$9ZECh2gr(vqux{z};KzS8yq0 zBWz)Ksyk@!o$fi;VSu)aY>a;NoxyIq=8N(63sg$}ss0pV;+09=~QHoJLWg--Y! za4Z_I*>j`3#m|8o0oUBcC_XLVE{fqTkvw{GDxmQDU0kv771{skgHeFGq^!N}^4|ip zxAj|CP1D?<@}o?#*I!-&OH!|TcTjDdMVY}#Rl=<^v9`6G=asrmK9XZ?;9t91#3#x) zT%jN)9k%2Cl>0>PeR9`5&p~)tzfl>#>JinX&}X{0pLuiJhj8TNjSIw9**n;Yvyy+w z&o7nEU4T(P=?355c(x4>Wj&n{GVPom`#Df}aTO8H#QORS>cY64SLXS`fN>!S zr-+T)sZS>=`Fr}?pKJbPDKfV@=bZ{~afom~$htJ*TkAuaPk>1fZ*3aPX15ozU*!Kw zYH3NOUPDG6Cx#oVk&dczn3Dx+72qab$KY6+LPmvH#NN47v^qzW`UsDp>Tw^9@pGP@ zK&^;ted7v4FrKw`_2J;G`_`r8_fPHtaBWsh!?%LKA&dPRfL!xgmiUNwE@C+M=C|2k z3r!QnCH#&m^S9JuDT6DW_ar4YNh8@Q?Dh%C0~{m~7T)EbI_;1=hf=C2T&p-n6&*-opRlmJf%;kMD?nQh;jGav!QhDO@Z z*%AWroFt|vOe|C6yfq|vP|_x~Xs~`{`Qk|md3BPv z*f8a^DydANjQe6nmXBFSyDy<(38wxo}WqguCq#ODK_yvYJwEQ08tuWu(MqNjjMFX2Kk>yC)H7wy=Q z#U*y=)Febk$y2QD1?8L(#?hTVH{tF{<0nM}Hn(=ziFSr^&DJ~x5_~fOrOG>ze)V=a ziO$^(TlSq?28Dp6c$#73RLwcJRvR_5p*V)h9oa1+%3sH8-00d6?f~ zxZ_ELEwI?wuOBD)jpje=QP6=ti=UuXVo^!|S0OCK*pwy`{OA&d}FkHs`TP7h8h zEbViJ3Q5hsR;4OBme4MPPv`R2BYIRM0TkCIWx?ZBoThKrqDBBRo=ID-@D-M%$H1u| zy{27GGkH$Y;Lm&QwL{sD&ZzPtm>*p|BkPZ@JFUh&yQrp1l!dwdvZDk*Z~O6a(vV?d z6CnwXIT3A2X{TgXIum~6%ZeZx_bffN?=J|Xz|Q&pbrYRPY~PU|3#y%Hsx_EzH6m3B zz{EFWT;0nFJXNke*#lvK2XJLK`_d}%>uwqkM^>zcs0Y*vOPY1yKeE)t&inYZDy{AA z%+2WjGET@vrk{JB1iyR6bGxaIeyt>wO|{YhJ>;zsr{eyrFTdTIrzEqSWi%~O7rBa^ z?uIrTZjrm{nU*~DE%{&AQ-%RoT!`z){jsT8fDI8Rok#g)w)ETwqG680<23^iwNDn$ z6Cv7Wlq9InmCajpsjrlwkBh}#I5EGA8-^)MBO!>BXcAArOfW7*y5eEMF6HPaL{P;T zv6k-geBvCZh&<6kZO$xJK*r*X?K-X=&vN*XoLx?8dYHI|uPDwizp@rKUH=g;J+o&q zjm(eOq$OPX@Hu$hx+Rq%XqF71p&J+c=!f#r(B|ip_4afj*z~jp0+lFqftmkY!_f|- zk>S(#d;RIVcL9wxkTS2MRX7I=?8Ojx`~u}Jgc)nJhH5Nf^8ZCghX7MZQSfOy$8W?B z=WW;a6t3#*kZ?H29mOubxpmU2>b4#~p_LPo0dx7gP3iBDMPN!O(sNcjJ0lxjo2^}O z@T2XS+wH)D$x@--2$>SI*dFZC__A{fOQ-UnDC%1roZdLiE8OJ9-z)}}rEPUXnH7vB z(kKUn++dG9pID>j+@k7K*#1z8R34{U*g`@HKrlT*Gn-FMQGBepqaWKzVq93{xuAL; zF+7y~c@G|3w@MHHvQav1e~(jdww)Jt@%DRc>c{aEx~aL~8RWe@)1>8z^xaZ{dKlnP zj)+Kbt;IDAQmT;VUwWC3j7L`IV^fLwT>H)X5v3Zjo4oLw%Nw?vDoX|tCz6wLiY^fb z9*3m=UW0=MOJF;+V@-v7C?8SKaUQ+ z!mg{W)}npfz2f)g;7o~dgvrYKg z@^T8|p8C2M7fL)KdS@X{sz33V%M{Lpt@c(r)vbP5yYhGLM=oIuSOdNyX#;vtv?O5DiVn{H^WUgvyAFWQ$@>xc z`N2Gfo&nUMBpPJtt;yZ!=-Xh>wjPYbWbczm@hO;yQ_xOU$$8A6J$COyEjAc;B2`#5 zMNl2VjF-U2!6d$q8fsN>*5D%y&VfOq0u~oPnu!W6TP$SQM&*8nQI0eT6>OWjgZvY1 zzLktG$Fis%J;!2^HuI-_Tn94)66FE3`x#d3ol1!6zqX?55ddKbNT}udxK=fFpfxxo zJ$s*{&#Q1SeiJwJq|S4PLH4?T8=d$4Dga0fIctM=%oToXq z)!KQ8w0iJ$4UC9$hc~}WO;*+leJ*y#Ea;8ZO6Co;Q7Uxr*-TZU7|=IYw1@oeUuFdE zP^hPy__Tv-UE`EHF?$KZovbft_#G+6x#R-QoepzYl;p-aksz%q#!Ez>D`@>eLH*I( z-<5BXo{dC736Fkl6M!1qoADzwDYn}FJ+8RQDo3Z(Y)Cl6r6ub9=Em3sDQ&asbw3=n z54zA35WPwX(inOK_2on->wr<%-L;_mF`f5-PWx({rn&sn2iegqcVAoyxR|tH7d-e~EYheUXTkma%$4P>;nLB=aQ0hz=xB z$`qAzZ?`A<8%X!PMeC3%Y#!pTjwJ}lnw?T5l@2Hk{k+U8fB;97LAZnZ{*cfxr}-h+ zf?hbF00U3Vh@L2H2nBV0Z)MNYVRu&k)TdjKu8wkxVI z{d}Qvk*}(O+xWxksbn2q%fiy({0J9q#B=OkaxG$~+$o+u7-lKh6FTw6hVgfFREnu7 zS~DyfM6a&>z5Z7r4fX5Co!LYc^r`C7(jf`KOZTO=^&s`vf!4fYefPhGb;3I}+KPNl zEO7Oa#4qReukoLlZKb~+XfwoE&c)%iBw7BqghKvR>yQVv)7bjhtUcD?YK7&(klHM* zo$vIJ)rVQHxm`hh1V~N2)47OAr7)LXuZ+`Z4lv!Cm68g;c6VVv4)<-d}=M<}Taywr} zxblOrI_CB2K++QTW#*;pf9Gidb*q^z`eVDpGSDEo_%I-*yJ5OiM}RCb7Zq8iFU|tM z+<#PyhT7lAFQNO5ez1$1dc3)OZ*N#Jp`c_9M@so6SB%Co0FKD@*J&iBH7h1Q)wJKP zyv#11`OvY3HTnIfVCS$uM5ZvGR$>= zs=%JCtq0PppLsoD+W(KUH;;$1Z~uqKF1rY28Brp#7RoXtsZ{o*EQ4|>$rce~hGgGT zSy~t+`+iCG89O0liLx8J7-KN|%ya6xpWl6dzrTBXo`2@`GG^v=p69WAj^%y4k9ndH zKVtTV?uq%jRN_m1`js|6{?9xm-7Xp8X$bpf7%VmhjZHq?&3_6!i+0r7Q_yS%Bgu0q zI!cgbe)e!DCo;`7*pg5L%Ltz9tOc+dTMed*!>-uF^??a+{#yw1ggO%6*$cUpeilA4 zMLI(}2-U+r9o>DKe~6|!3(5byMpr^ZGA8t(DbqnLKU+g97>!8@QVPKkX&KT2d#$cU z*zH+Up9TwbhL8WOC{~={e6@}8>-#5E_qNju*f-NXr}&Xd+b~8w{!YWJNs5K?T9H}i z3H_yKof?S$HX`^S01S4?KPwh;KESffz5xKe7ii+Qj z{L1!ROz>ZcI}?z&7n`VUtFjna2&J-vjwzCF{c4q9gnt0wI1#T8aK^8FyqAAC>}5aa z=D7@11X@yqgmu87w$ZRRbjEi3Ubr*~7;YPY?jv1*P#~UF^vv1OHEp1*08|ttkN%Di zR(q%ODold-H)uMir&7R6e@K>CHx#)0u81~uTMcWW%VD91C zI?!8k<>c+3kG6(x{Xk9Mf~)-_8p+|G6!SV0 z?)!k{-WGc5QcBh7?*d+^%leMU(@(wgeR?;-RTv`ts4|{PiV$OvOV#Uqg*MFKyJm-v zM3*hQns(mM8*j?hYLR;KaVGeux?9pSypiIPrSmHQJ-w^Cu;lajMp1;&-d4bM4%J9U z(k=J(_wT$M`F$}1ot9Ju^|+`+{6pXVlZyyUAmsV;gT%#u<-3iHMskFL(8$||7VqLh znn{`nqrmI@*BgJ`xkU&)yNSmsYzMJF@5rSe>iz_yTR%@y1G=HQX^`0q zM^IEwE>u;&`W8dq^zE0LoJ><-|KoYNx4Y&1@>(>z`-ORSa=Kje>xQDMJ|D4Da&Ein^UP`@_&Xytw+f3R9PK#KrA={$9NZD9}=3?O|vlHhM3P#=G!Ug&3{#>{IKc<4fMtCbdsE(+!Qp+_|~*<_W&q8QS8pu~Yqx7G=GlOJRrA9%^|*mnDpxd(|@YFbz_z z1|i>n6kL08z{9hcD9bN+u*fX#Bm5V~48^(mJe(Xb%|AAFKXjGh-JKXQ*=?dey_w46 z4R%}Q*e-l9x_>6jyXyOc`7)Wc?npixxt@K^W*j1PUJ*=w6w$UAEyG4wxV`a z{3aoDnyV{Dyu$iCM$GUQ6ndNdl(o5fSu*uevYzweS^@I7`qKWvoc=s#=MGORJ%oiN z2Pw>MFD4~c&>;)%+5`pw!}V{~|B-gdX{77O6Hou3aXuJ+#A(u*Ni@d1oQeutA$ zF3v&~9d7vOr@LlVYo@W=#fPJ}=hazN5yV3)bz~h9LkNTe^Wd!&jCx6M@=<5cHv4 zKkjUWit&tfNK4SJ(4WPSgrwxY@w?6^%A%csO-nCMoZp|gWBj22`owr#4aIn_ti)*D z=g>k+xzZz@kP*w+adCnHes6>lOq#EYi&<<=!Jee@wNo-7&ezJ&V5EN-VtvK5O9wkW zTG1J15;b40S3_#?)$4F0NoW_8cr07+T?Cf)wkpb^2?PCiX8bFL2lR9HEFb90?updL;r6l zthPfBUs)UsTyXtU?r#%T=QTj3SAz&gdLzE+Bk7Cy}mN*UxQ-4C=aT+ZhL-3ti6 z@M)lG;JYmf=d<^7VH(nYZUEB&Sw{+N{|+DSmhYaTvAhIEuX2UYP66SKwEiC;u@2c@ zpa*h*6}b!~^MlcY7Wf_>P_DqRFYwiNGrq@>Zw}DG(sx{?Ul7c8Tp(WwQou0P>)tD1 zZ13W2m@>?R)6|d#R4RDhM;wGM@L5-hovdyi5Qk=Y?~Sjp0*G(MTd4ho9e(a^Wbl>0 z;-mxap?-n?5@BQhfR!kj5CUMwu8i6Ofx|;w#jQ0}W@DQcWF1vC8REUudPDm#;;v=f zIk%~gA(JZTQ@4CEewzCq>*CPWVQ=a-B=DCnlxw|j6EG%S$+Ykl;%8hqsogt!j~^qx zcjK(QmB~h>?UxTlTdZj}dSff+o}y1aNL^9~`L}OO*Up|GjgBc+JC;Alt#g%`__3C$ zED?D#sNAUSyuj;$HJe)1+`GD$4KmRuEFdYtj{H0R0(tK(CZ~ss)dlUkc*Wi(}d3|TkLl= zbG5(cokbr}OuM?WNW#7#suuj3+VYl=QoBg)PO-YoDaLB0a84UHX^LQy+H94KHpzrU z2h3ff0L(#0L9XR%6JyH80*K(-{G)AikJckgPBhinyxxLCY)-8jawtSzhwYrWfz8ab z^mAu+ZORIYn0Y=smbdmIIkneEbTb9@enNv7e;*yrk{D2@adZ3>Q;T`oIWYKCX(9Lb`{%K81U@nz#zb*)b=f* z1JmWt^S8%i$e`jsp_&C&J*Sp@6#L4El<47YnG(wL z;+YTIIj0X=(^1>@dE@L4*b11*uOeVu7I4iPv(iY=6)Uj z{`hG3I6p4&yW+eU&!pBy2yO+Uc(Y`@B$R#?e=>4xV^*YoxC}Y@Gfl9v%7S3F;#Ao0 z_{+yu33>*nnX^^ zOhiD_98nrR+r%Bc;@^;M?yM>0I4FW9e!7bTrqpBmM>pt^FxQ+Dj0@eLw&VI#GUNmO zI`!teph_i_>%#=EjwQ1$vkL_@UW`xB?dinEtcN-$^f}T^KCRMy2PE8w#Ff}vU}4kX ztF>U)tZ1g^dzB^5e#VHC#{y=m%|f>7IZhTFv@V{ww7-|ZHc*sP#dVxT59Jz<%TE*L zq*A}rWUo7PgCC`LtTgA}<_Xu4h}?G~Oeri42GV@zg57&uz&wxY008a@4S*cHsC~A8*BkKGU3V4yC3t`|NV>LI_pQZYddObRMHAwp*05}pg@u%%{K^DBB zLuV-yZ>)U#BlhKuq~1AgpBY`;>~dz`w~h1NiC-_A1$c{qRe;__*jT|_ucQW_0Z(xs3qS^T4Sxq3K3z@H^N-=!tt|`P zJcoy%Q624&)B3o5oAaZSgF#pJYek(tQfx9;4t5`}9itM);KxcIli0&wMe4J7?CN1- zI`Gp($_u~!?RI|Cp*3%43p|!!)yw;~fGGQATt%hV$-AKtRw4<^N=5FpQxAbpzX(Zp z07LY&57K%@<+duL5*EK#;0&j#A|TG0lNBmF;Z2Ou#)NGm&E3wCP*U`23p=8-+sJ|s zYrb;{y{Q?NQmVe%%|# zlB}i(!rMkF!zy#qaUoJfjOP9SE)|V7$P<{G^2yt&hV>d9Fxj;~tT2mWx@v7i1@n+v z#Sl}Ak#BuZ(1EjR6dZlt9H^mhfkGVlSygXp$M*C8AX2>H@+&StogX-iDsBRY>sbGV z5;rwqNI38G%@02D3Dc1k8cnA)^>Oy6{A9nVD7_0tC0;YpUZYZfPeLCRZyx3W1)1b- z`ylATV%O?Vgy9E(8v1c2Egph_=7y6?1R5ZwF&6}8o%N)FNoqQe!qNisB z%|`n+YdGs`SA=VSRO}TytB+Cv%J1%ea#~;?YD_#p@7KC{2*6o7!Z>t-mMba-+o>Fl z`gzM{dZsr_V87-1mWblv=EoC8GS>5*;SGP2YX#7Sumr*aO}GD+YiQr*uP%VsK^MS5 z3aRRPvAouoVkge(Qe1pvO|LswGE!*i)SZwKMaY$Iz33N?mOi>C;YyS57}f6}3!Xl< zFw0CU$9Nu*bDb*!v9EV~%{oStl;AcCxdxcJn)}5sgbaD{b_Ant_d-gBALxT_bEGULK{-gq?3K0yi=b=7^twmB~N zmzeuLY0(3sXmj6l_(`o|0p{F1c^ZrLvhFl$yKk1sWPIgMDW5Z`^ud#XO>I28ZQO&V z!<~aAFv_OQf|QOerMCPg?gPyG@yw#wNmo^!b$NI4TOHG*cMHvQzqcT7dct?LUI42# zAY0e2qS3iKdlZ$^s0uU}2K$7SNLJVy1gjW9XUUt2HFW2FO4&=Cm?qyL4(X|Wjor?- zJ}3nX02*~JRaNl-b20T&?#AISyi2Z$+=uViax6uS`P3FhKz=}<4-9sDP}^10uW!}c zjkAeG@MZfKvL=aZ-MpJ|r`PkoMHrF%psxcxrY8O&$Yp4WJ=7bHU7b3P)432h`|?ZS zq8x>V-@3WOaM7J_RtMW;G<+i~B({g(6#TxYORo7Al1d}Z4T?h1e}b7So{bc|crqBd zyV`lF)bwdAqiy))LUh$O>10CZ*Bdm|p`C^CnWD8@W-9}FHx~wx5a`{0wZkQ{A|(~$ z`+7hvDEy&)#fDH~AW)R-)~5b3IE)+5vX!fQbw~B%%tyuNEY9f24Ie{#s?-d8C;@px z9v(K+x+BIQZ8Ot~DYdY=@!2psjd8@opl|iD3K+O~nd3`+gCgxnoD7;`VqYa05L;b?p=Gh#hTKTz=XJ< z3(7nQC98^VoT3Qgy*;u}-9q}P8w9AM`IC|63kbVX`{TEi-}>iV>8;;3cX!d>D6Q^5 z1k~nZx4sjecFk=#B@%A-zPuE@`2bn%Ft| zMG?W%d5L(*&0f}tO=qw|%;WidDFDYB0r?v+-&&k$#AxjDRIkRr1)dkjwDhFPme^&c z3CI5h97_Otw?oQBH^Tvtek?G>$qv$BF~H7!LI^a>&DFH6Fr>kt8XgYF$5a)Lf)_xU z_nuoU(8&#za~7BNCV*|en}7!d_YL@v941#Lm|P2+R>m;)}eD}Gz5x(TLw+xEK~ zEsu(?^SK5e`15T^&w40haIqt8;>KPG^P6n7Uf<|&Tgn_iMbM=!tK&`#G4_|LJlT)KI5{9Zbunoe&6 zWqM5DGL5dC8=B8|*p?^+`b#{0wWH+dB@88o=DSFVsaJFS()sFmIW~XWF0b(GTdjn5 z`m`&l9AH>w&z2L}2!1D5y(1t{QR6yFVh}Xs(UW?eecq5Haj&nj`4NY{#nlg1M^nA& zUwuB>o=NI;(elYwpKLw5m;GoUbvrUe`{G@r$gYiEc3$CCpG{c)9k)j}I6P;K7 zo(EKyrns^Gl0OJw)%4)JiZDle-JC@GVdTEJ(8x714h{~NG|wr>x;;v0SPMFHM#OZV zilv#rZ5UlNyBrnzE#dKgigU34>~(3~{OFHOI2$p<;CXfDD|YvK3}bsg zb=R%e`V8i=;nnSMoWcWr3nSmv;lbUkFKRmaVRUQx>5 zdKSuXamMVgc(|6&1S=*AY~#>Ytju4l#${SiTV=H~FElF|7s#u*oxDO*&XX-+r3())2J}o&9pykpbB)C?;S~cE@LoQ?|Phwb>7Bc5^FlF8*=rZF1y3!vYLv)_Ct(et| zg}@88(0|^d>7%Q>i%&G~eTT$4PHc(=OSa>m9wJX;sCbp<+iJ;7&#|g`>L5%0FQqGj z`J$XA(RuAinD0yF`E|;uRWCJ&GHH4ume#dB-`+{9N(pmZOqC>O)i;`=J?^gWhQ+}Y zVyHKcF#`oA)lmThC~r;3tuP*@f9nWb<~RX(YKvOwCB7O}&@Wn2FAqrZTPRCUJ49PA zytMb)I(X^;)KUlZp+co@xSo* zDO$|?Y_Li{5Er}lWTKC&5;;{7K;(>;&FOB5k^%w>`nk4-RC)fC^SP=am3kJNVBatV%Bskw=2=yLMBlZ1@_xW7kCujFNef2Y4wY! zTxwF8ol3gYY!^I#G$l^+_1>gp00pZ?N6G#PZ(V-%YlnYM*H zfgK1*OoY1&rSc@U5_$@8i!m9K^z{u{@avxq_z?8M80UIw+b|MxOei~tpYF1$icJ&ce|J?fT8qcG1Hbg~>dTIeoJhXIhaO3g+J`z& z8NNJYmdR^B2Sv3=bC;t@La2%>Kd&<8^23Oj^7P#ei!%Q*2_qNiCF!lqvHK2kqOgb^ z0`^PdR`3!Sg&3dHVcjcvwY!&r z&6i-%{5-B9JUB;;&P*G2Jy5PYlmK2fBgy?H%`ensb|qK-+^gLA4_2MC!|AL1Y3IB{ za-Ot#F_TMZXsB0vG;ftNrD|Z{rb2O5m71pKXyF7ChlIcbZ*LKFCALxL@vqoDx|};t zc;4=?bO#Ux95&qJ+cr6RsZ*CloiTSBa%ttk{1QMI`{Zu%4hOsalStkQ#)0*7c+et; z@}zV*9aPCXhth#hgl3D-4fpiAllk^2wZkNnu@)7`X;hS*XawrX1?{DD{h6m^x4D`f zJf4@A>U%~FogKWP!ybG<2(}I+$KoI8#GR*iEau@rw!VPxuLd%euaoJ5Bm@ zlBTJ^_Kn0Fz&6!HDmh`|0q7o2IPJKVI}qY23Ho9|KvjQ>B<;S~x^Z|Opqi(*TA;&F zI;T-!ed3vkW>$tv6Jn$a?5xBh&8{f+d2V3wwO~+%F%A(!lkRCd{D-;e1)j5%Ydi~3 z7uD6TE2sctx=qVFObY+3WPfdi6Om&h!KeODz(suR$-}Qa%EPA=XG7uERGMCsFKmom zaCybRl)(SOc%^oQB$_6b-w4x1peSS(me1LfXUH_s!w~Q|{~*Vqq9eSwelme zse4i^F2iBSc}idz0ZXUcad6rWJ26xzWg^Cs10J0E-TUFOVLjbkp;(jk8LfCkE#co| zCxla4Vg+U6Q@qqTcq80^NF6Z4>_1N04cr0e2CL#^B4EuhAt=95Le*~6uz*#=w?qO2JW@FS&mZ0FM#E!KnM z2XrT9+`5BY^`4$I1_TV4AP=+He0+zNOqgqBMaEN1n-&akKo?mF>_~qC8)@%UX{tp? z)g?W9Z@u?V=G3r;ly;I#Qg;j|8c+MRa$@1y~^ z1d)_6moB9)kS}RxsVZ>e`)1WKI$_ig({)HF2U$1{Qcb%Cgh%|>Yo=>7L8XsF%bI@5 z%qhmYJ+LYR#}|T^=J`ra;o90_iXoe)&$h^yby-|j$XgN-f5s9-H{7v&aCV3|)4CV& zhsPRkgr0Ou(~QxpW|_k8A%W0Vzqu@@2ek9sB0Nel>+9ItDU=9T_o9E?s3Jz@v<^l3 zP#WK#lPjKP7?teViW}v8=3Lx+AH-zZcIVeXSLE#R+YL{#OAc@Lr;=YKWc&6b>aM08 zI(&FRZ}r9gcqn*bM6LPBA=)L~sjFDG{4HtSNM|3#mIFL6K#mmdEPhXaDc=N{{&r^g z=s~dQ@-Hi{2HHT9DVBWWoY4m!(B$Og9n*BBxY8F4y&3{#3!tMG2M!;;kDKQ}*H0V< zK4_4~&IjbOOsf>XRMb;>k$6v<`6Ze>e~`UioT!MtHSK;z&{odkI#6*HzS|mk+M?>r zlz*gqg5$cI0Odk@G>)_KT&KV3l0{}7vF1y81?I?%k+gvx`2y*Gn z`INZN%nD<(Svx&W<|uQUWZ?rMVWyW&D zOwaRVUuRhb3V@1lZ3gevyhUFdnf-X$j;{&bd*;VKNW?&xnKGk)s<=}`UyO4C?xSkX zJ2Pjni5@qKQ6f#tUj~9H8_IbY20DAnD%}S^KuEJ7n}_-b$KFQh2S~KdKfW z|NoIp>~sH)lLH>TVwzj%Ot_Pkm6e*dhc4KM&p!0>O8lCwr}O;tIH*#q956PQA`~#K zP?A!gX0P-6mfwv2&s)ade0=Gy5&F`lY8NAyipx3Hdbd1bEk{>j$L@u!TPWUA_72+v ze1g6bJEJ0kFg`c_?b`pjpnW^m{r#|c`cEIDvgyyWCEh6$O=Ze6wr`GCo&s)XkO#z~ z`uhJCEx7dmgJ@w3ai`*o3mJN8*hc)yApb`!ptf>AC;dQelOIlg9?KcsmN}|AueQG> zfOz_Ay6gwR6J3#l4y)+?a9h~5xqopt{tt7n({&$hzIbFP=bOl0a zGh#LF`lFVtBo4Guz^O*&`J^@LmgzJeZElIA`+FA|k2$JUVj&X%g{F&j*tI0c#9l4+ z-%0f!Wekb{SiN@&z8sVJO@uZ}2WFh3vt}GV9R=Cevg|;D?P~26%dW6$3z**xNYGpq zjMe_3X3mm+nAbd7*U{GS_hs&I^888f&#+nspIo1Pk7E2uGkJ|^c>%bN7X1_u*{^T| z7lx88jR5`(Hz?cVBzu9whu{2mDh5{pzXfz7*nNS*7f53H{|NjaFY^C-GoqREcj%P` z6y=O^!NtzpfKyRkB!Qbs97@w{PcT20F1eDk|K?t#SBvEbre0^3w>G1{z1xKJ#$beu z8VP|RhBb+ZVd|IeB%IsA7Z zUL4ga0cmfQ)$b&j3p1y!Gd^&Y7=HAhwDZ1zj z>4`Q-6DTB8RjQmQWU8VhBZsaeIB;Ab3_Fabkcr~VEN#WW5v|4fsvLXdjsHj*SLs@# zrC{~J(>7ONEzVnMTYo@S;W>)e0uHBU$%uu$2itmjM@`w^WG98r9$#292$m7&84N)_ z3DbMq!}g+^N_<9M+phSO7`mr$j`41M#&YCuPoy}>0JmXO^WcxA|8T0c7#u8oh>)>= zy$jS#fTOF5`0S97e{<9%`sV@po#B+?UZhVw#Ou{t)PAZ#M_qU%(b=^dOetC)F?x^z}&y@ zk0WAuulaM?R6w509dfyKhY|`nm#n!WJlE11OMNhC_gn z9@F+dH^AK(YzhL6lEN^jGWP#?%7z`+5&$2CRvU)}vjIAKz&Xm2%R;-#RHdc{D z05*7gWmotU;1NCD?z7N6O9Ahc`8&ClqG+CoPVzl&3VHg9qYS3rSf zDbis;#frsna2U7+gEImLvN7H)z5d%g|EZh!{U#9a0HrX2-P0ML0TgwS^+CaIJZfnv zfG{1Dk>UH?(!IJB{FI|8bI40EP`Y3Wta&)Dnbr~}|0&eipsah=CDM9}sC=M?{wq;P zGu+l3tQbzpTdTe{@2IOUG9pkY#XN>Y)g1|#bD9Xg<6m9?_wpWsOxvm-C7taH}t z?RDn{x*jnFsjnuI*!}21VT_W?mAk0Yl02l4Gp)LHdEcgoX5&eH%G3Ybb4DDKHQyzh zV?u7@{*%UDU|z`za$bv5GVT;Ci~?HnWiJ6qHz}I-{T^qtS?8E#Ae#xve^Ru!^nBht zd&j~g5Z5Cuc;#tM2k5y=dWXElhkHMEdYva9IDAGndjzF~&5YnDF*QMh(V6F<_= zSbV$^_d@;;RS#fO;0fkbLx7O{ii>KdZ~K7t-U=UF5C3+S7ohV6okL8$Suejin~dHn zFSq2^yw0R*4-WfE5@jP_pj|7l^Q0)Bdk4+7q(p(u=3(2#CbI?L$2A`*hj&E=rn|k% zqYVNDS1EKm$0vjPbUR&oE3R7mEG2TcffVLf=Fim!MukqR`m21u% zNyz+iVB$Wj|@T*Fka%_wrpaDZcoV#P%qP8;PjJD@$ zL4c)*t#S(Mqu9}ag5Awo{SoX7mRV8cVUO&Fv0XsCJ2Uz;8?5}i$oVeUSN^Laie}om zzx8NWl$8q!CYvUo0skkxl4suA+lz{S3smY7C337r0mPoVUdA#Di1!+= zs8ZBFzy0lnE3*aI>FYQq9C4=YN5Xe|qa3L85P?=v=Z4&*RRmKbF&=*R_Bqxf&e5|s zH!C2YIQt@x`<6?aQ-dpdUL7m9cYN5SD5CcAz>~~e%&F+AucZDYibf~a1JVW(mYi+e zIbxPKS2!>Yp#a)XOd%aO69(`;={+N()BoPT%+3Dt`G%~3cE+02)OpH-SNhzIAIq*(+ALZzlACch@{k_LudL^+T zIuH@$@gbx@C4C&}iJ9t4s6juf7%1!b`{VWXKHd_dvi9)N(t;jl&LMOC)`h7I8VLGc zR`(J?e`%#)D(nh>%U1P$K>uqwtCR1vn6F9Gec{e27`spcj~qjZ!W6lTJ-`!PQu6FbvgZ9F+%%ucCa_di@Sl=LuPUJ3rofTNUl!I0@FkTw8Q(aa1@dGzo z?@Yfo?k0?~SVL}Z4hn;EZyVIpE%1m@>K4VMbA>{g?>l2w(@t5!<1rcIE3|EW@DU^y zDw(|(_`Zm;Lx1ubYu1zx;yj%8-h%Ar(FnJk+Plz^0B;N%|QJ&Ko~M&yiMx9(%kL8uC*BdkC?M)`<_j7ceq zN0E}<-&JNvh&e5r!R@5A|H=gaKkKdX;0R9z&s-7!9F#^7KI)EP-=r@*XXC`2IXT1vGA0Yt@BIdX?v+#`!H=Pu$Z_F-4_Ve6O` zN`?-Cgxk~7FQ(GYpudsgj+s%#sJJf#t(kxRaDbE`VoR{Fq5?b9?nn`i08H=kufR8K zE&654<#Df>UF@!N`6&eC&Ip6J4XYVF*5sUG#;Z|(x4+x6wD}qdJrpQQQuF-#o4|uJ zKML4$xzwg2zMF+G6 z-n1TX=l%!^|7EeH0*OK;ANSP7;V~&@DD=NMt^-oS6+n+uOrec`X~llvnV^@>j~+eJ zDsRRL2Z98a%#vaCcRx=gU3Xi5?XQ}|3>{5?_fV>q5Btp)6*r|oZ8WSRI@;IY4}BbX z4CCr-T?8Jdfc7NCu6}8QuR43mtLe}OD`4)Z0UnvBQT~&d@e>7{N8`aYeb|)(^v>L< zLmgq<|2FFHF;-3p=V&lNTu(C{{}~rTxp+@zxxijRWGQOvilU9Tx8Uk&T$6|4ED z@-mmrkFyp#F~)A9FII|3Yyvj+G@|(VTuTQMyK8B7qT&jidHz(nLp*R_)BLlsafM0p+U9{Eh#g0)(ew{El=juowl!Y^c^M; zb9_oo(K{IYqNzJYXUoYqR$hLm$gwgkAJav;xd9$^3f?^SMAKke|6CBN-_rq28Ly1<^kB4bHX5@xM#OO zJY}i+JF09gTM^6Bt-_$j?gDg!rBB}Spynr2$AbpMwi`G*Jc5DnP+6D5;zlh9=Gl^wznllB>u`Q=DD^3ClqrT}xJ$X78D z59dE5FfR$^(_;J$!h(8N8rw6j(Sl8r3eeGboRTIoIwd6&p^Bv_Lgl^L`f%zVqpkjg zA72O+0$B4E|4FlZ9ml1@vYfAIlKbn2$k=M0Cx`Yj$g5cGCzX509M=2^8RX4#E$sG} z#=6j>`!-&6mVLK8>t;OJJJYLchiig)B2Wf7wUms#3{>Gc(}RGT%)-2sj11FrzU<_c zBzhmzQOmkI9_JG$b2Cf*}$enDPU@+yRm$4sTPb53*`OwRvE<{r2rxmIY_}7Q8$K= z4PV9^t3jD|1~?_=OE7pxtl+{~ z7}59r0lxTx*MutclZpP@5&pDGQyRuw7fG{{PBPs0eR=}d27(Pty%2Mk&!&9H!Y+Ld9S*$Y(eaN3A6Spg*_x{c{HU>q^oNe`?*zod}# z^=r*(Z_t%-#iK;BL)`)n$G_aDV)4H2h!Wsnr&;t`oVd#vo=2*Qbc@&T_JiJ?wpt%Z zU`mTeqep%6ddFumWt#N7{SvqKwA%xg?R&P(q7DNy*TxsrSjo&0Gh5K_Q1XZGO1wbO zvC7Z+N(a8b7ko6ZY7B74lg055Rl=tA<-@8-Cmf4LRXm^tvkoNXb5h(|LxS5!;9QfZ zq*n9F9qD1Z2{(!^5JfL|lQ2%>!z7Z5XiB-l?&5-?c_wpC3gWpITTAwBt48F3lw3$n z3Cf=!B%JXH@RH;-IM+?SAGrg34zP{yZr5I_v$f0-9hKsNXhz9R?q7DY^qE~LN%=ri z)$Mbf7bJ^3(m7CtCa92l!PfwoA@rOiMU*Zh+~c z+)F_fqP1qQZ;$DdXM`e%0BMb+^2=SQq3X>QR;bJg$KGc1D@|n&%U+xi;nUb!La}j+ z#OjFfRRyoEqy&c%W){A9_;ij#7mCh$)AHVhAyZr!>y+I-oe6m@^rws z6hHgT3~_xW=c&OxGE%5XAQ)rDUA9w9km7ltifxYz%)nYiRnR{#o|q5{pih;*R8Cen&}l zQ?D#CmW^VhT(T0CC4zmr^Q>>AE(1Q3yMnlYAE|cy=<``wegT~*z@M(NTx;i}yZQl? zQNr6b+qqypo2{|5F;-m|9jaO#He7hV@Fbne9P#sYup_483mXLy z_Q1$s*E%a%bV|Iik9}$KoqL->jz*@cBQ=!CgEbYQarB1A%79Z_3a})6l}ZW$=S=MB z;yju|`tw6H-Ln&lqKw-pn9E7kL$Q!o{vMbaVt9CNoXinx+HUiz%&na$m!H!k;e`A4 zq_~3UxK6hIj;F!PO*@p?h%eU(n9%tpB_HYo)B6k=yzhD%LX$X_ zM!c{TI)Es6*Y$3DYcrGCO|;`{>pu@0wI2u+RuJ~D4!(K8Tl&=6h^f+uqgNPIGIbM3 z1cKj3vlth53WE$kAgmB~8W1awrI*TAl4!jd9Xj@9u%{8i_w49QmkkQD3;}gb$Cd?i z-Ows_jOwx~bc7bz-9tSuIZOYo?7f1Q6`fF(0e&hqx}DLApWj*Rbmn9GgwR+%W!G1} zuJq0Z!ebcA8JZie00w;E$cklqsu@z`4R$6Sj~DDDJg(0j)?ic%MLcSjJn}Q8-4i{v z?TTA~myh#nKl}Dbl5s5q!b3p$gXK5T6EG1)EXJuQ1n@eZ*jeJaX+1N_d&!m{m1&0K z7p?Op0dz%4$kKLSRH)b)TYaT$`nvP|DGn}pKB(}_r}6bZyTY9#b5y>0Me2d~GPeM- z;1Di-z#myt;z%^WHL?ML{#hWBxnNnpA&;0j$7TTd>hi4DZ~s+5WDP!W?r_O~#he3Q z)in>q1HF3)^6pyvGKd>q(Y*iWqsF7<>+ikh+ytLrcTp3_-fo(T(V2hnry-j@wOxLG zJEcMT)t+Ldc((bmwAP{U^?4+Q zw6NG6gvG+@IXG#}Fcvw4F^YPnerIz4-4kw*on8Rs<&TFQ*7j|6pJ+EDcJ}7#?cz5# zcH_^OMH#&Eme>Dy5jyON-NKL@`ZOu9}I^GbKYms zYdK;R)LMDHzaCjQyk zl}BG(0IytdHa(^iBo_Zj)KoXiFMtZ{b!(Y}^N{4=L>DDJU#S_?N3e>nKh8c0*sXw> zwfkhbW-@O>uL1aldz24CTcKKgM?P_TIfP* zDRQ=)cpgqyr`gu~MZ{de)gtR@VLrutyN%b^h@ziyu=fo-y zC$e^XWq?Bn71gASxj1yuIs--$S-2wq^l{v&i~6IDm$<{{L^s^I!%4wL8dKFVDW>)? zl;dF4{^G`0aY3$d++tC-%mczml!rdzIN z6T4?X`A!Y+3;O)}9ewp^6%GVB;3d`c37-_zo(Le9xK8RXn&!E3`I5-{XWlhD+vcwZ zU4g6Pa@fq2((o?o7}+`jzWBWJME@e>O}P$c7iZs zfV>F-zxl?*e0c5Hri!+u^13MD<}nHA!c5!uidkh5sE+I~qY2xiyM=m(2}?j6Qh{Aj zee|zL3PGIr1zu5fzQ5aZIbWEI`lMUh_-dYz8y8UD-SR{f;{Yy=s7;) zPN5~NVhfS$Nj_)--)VJF(r{ge7CpWWIn-yIiO3AK*LB_c%NEcD+7>x7M*8R(dM?x( z%~)y2nH?B|^h#lnS%Z_2X5j=T&g4%xym3M3sBsFj#S-9>RgbJoxqb~5ae%(x$p>Y2 zf5L!)bgM91)CP zLI+$p(*))x9oMCsQ#m-m`|amn9nPlyvEa$1HrSXk6Z|7t^i!NFMv1=kWbk56Xpd@_ zX*AetM&<)BOp-VBOyBp)w%FRjbG-B7*N#Z>M$|=XQ3T<|F`%m1#LYDm6?IpWcvyl| zsJR($`h;U|OgROKg6hwr?e$OVqo;)+TieD~6?&xkILKLqlkyx-Mdxl|t-%gcICC1qAOKDz*?t93( z{QPwPaKP%&`-^cY0X^VX0duMN>G`DWX-?wa>|`Eq0&-t8-PyiQ^~r4W)HWQZZ$`CP z+HOD6_wr`d{J;^???W5?bF&a#%W%V~*rFfR)vL$TMO(GPT_ISs4oN4&s}WN zQ5V=6+Zw4)IVvYIHTw}}4iVncmnzjjY)68xJb!bn=KrDVO~aw?AGcvCNv;ygRF)|c zvP{~DVM3B*OJxZ|DoMze!7SEfFGOV-5-PHU?8d(DMA-&oY=bej*=O!=*YA0r`~E-2 z{d?nuI+~g9(J`O%bFMViM;%9IJ!;mjWQ93PPE1ZJt&3lIeuOa{NEVRg0N2Y|Yy7l# zzgCl|)TA{`loKkXedOw{fC|t_JL^$Tf06~%2HHpn(oS>DKed2m7nvZiN+oV=JQooH zW6BoQvZBuT0!_sS>*j}y!hZgIis&Nd;d8iw%pK^p8wdz_?A< zT2GGEZ-)&Sr6`M5Km9ti3mgr%R-m^v&@AbZUelqR=x`Z^f$@F#*<39Tg!lI_H0irq zxP9HT5VLWNcR>Bdj47(wY0x-q9ODI?3+hUL=HM>ld~*FEkFMkq&U?igN5~pY_&o

I77TGNjZ3Bvlz}M(VP>RPi&+fd{2Io!*x7Lema341e(&jg$BE*sYP!~ewhjS6E zz#!Y8Y<-OkT(Yh91M9lCZ^BZ2m{b`!Buvj;`xx{>n4>6=qtHYLJ$~}SbPD0(d661rC8HK9w;C9w(ftvrDZKf- zDQ5tmbeyc#u12y%M1n|>wAAInP62=^p9o{xjDKSWm8BhK#{0g+FzV+;VY1;c>4myN z^7^X>AeHIzK}Aj4fn1M3^5Oas%c?vlu63zN+TI)~w2EbMp58rkj)jWp@!5YL3l;aJ zA^KmgGCyir^o%W!jTH1D==mPr$ew@;>7ukki|>Fze!(%@8*+7Y^9?N*ZNb8DxdK{x zjVmPWFxV-CcQ%J6e?~r1bQ@;bY{F#O++HN|C}uOA{w{cNJCbfbj8U0Iq=2n)6XYe{ zz&p@Lm_ElJM&dJTpF%`1f3ALGGiBLlm9ptrAiv5uE z=e=)4?Uy^L~g09>iu;wJ}(fn(6F1l%g}T6roJOgPr0esm0> z3^Ss!X#V<~DPU)3%H3~Mx(XH_WG-+Hq2cQ9S?_@upwbWl#qydm|s*v^76h;DM$;MT^!9G zJUugYyQy4sb9ZBE)}Mqcs(MZUAspwZ-H7(!^xcA9Y?!C&V0_kVCmy!dfr17jZi|sU z%8q!V_|3UyN&88^vm5o6+lu}SZxh8kcpPU;5ryPk6*ctoHnZ%_dOD79BFOyoy?l8f z%F&T>a9m$*syxYnMr!A~wUzovl#pykul4SWdybYeE^9b@_g|05 z6s`*Wiry6hyPn>t(@5I+sBLL}`&o}O$YmWVbmP2AEO{>&MX1h-rH`4fR_3b)(*`b6qFEaNE7U@(*LJKX zkk!w04u6UsmH25a`Q+G6tNgq*{=h*>Fk%YGZFu%6P*Ax<-+_96CBeaXV@JD9(}g?l z418S9D%%Te?Ekau$8F>9f|jA-*IOvdehXwrw3EQm6nYJ2y&E=L@zoxgi9}o5eN^9P z(0X43asz5YLO~O9K*uYLF)avI+?%F?{}+6N^F0cjC=yXWKi&W`;U&oQ8|r(v7eIJW z@1tm_RkBa6tJ-ER?lk{uyn*8Gy(^_Kci`SgDAK#_NH$6qGocI4YM1harzkrwiznH=6XvikIwSFgk=A*sRRq{xvKvSCG+Y@sXTqBfE~kp4$*Ye>c{U zQg@3LNw^+)tIdE`{&Z0n=7O+0vjAhW6M<7SAKdx!1exQ8LKSUC}N#F*X!vJ_`=oP$kn}V{`BnHAdJY_o+pt-BaQw6G*c? z@nqO3`lnAQ>Ydq>ik~pcn%he=fR$jfr2Y`%wCTDuvsO^;IAjl=VAA*%L8`n&aP>xi z{}{;dGbiTUh7(4fd-&bhep)c;vvF!8b7Dqx?PD-8s7K6AdU+f{?CT#06W-P+@k(Wd z`%v3Qdgh;zBo-6%Qr;N$#OByK@y*C*{4{Rdes~#(IP)S4TJ`7;1-XGpTtX* z{NB@WA44`<^)`#!r51$N(|eO={F4su?E(C^TrEXb z6ml=_?zr>Dv;-Y`r?%$eZsyZB^sEFqQ#99%EevRk1-@H~Xhjsg&34h7&EL!#O4GcF zj#lf;u#<8{L3 ze=ks)#Ye4u5A17?UkQ+a?r|9!^FskUZ4hP$6MMHa$P4?bCHYJi48Z|KW14B4>M$Ip zr(!Ye?1jDCJ0`WdQ(a8axp**k*IPqJj^W-0h=94gc!<~So363NvwP(J6Fj-VAW=3W zPS7CGv>xt5`~l4AeV`oJU)eC9>{k%;Hbk=As{}Id!`jPJEtFe)U$ou&O#qE%}Uj=lLn>2=6j^J_)#n%W2fG6qR))yay^|N%l=OFn=k@YaLe2QqgyFS zbfBf=J!lNJ>xMDN^X9&I-{h>5MLa^+;lb@|Tm?7Ifb;DB{y}A#B5te9vellIwtL!7 z88~n0j|=mT&h!lQ0c1`Vy?aEQVIP^_27CGcK<5AO18i6R=~-t!KK#hm1MO zL+)=B<9i3!3TN31r1vxXM|du+N?pm>%LxCqD0S5alY+@H>9oC

*f-jcB=Vi)>C$ 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

t8zR3|=Koj1pN`+fFhOKjUvO7ko#8Y2hq>Um@C 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�nAzPsWaM`i{rI_zNY+cT0b+Yg;o&oI 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 S>8MZK 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 z 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 z 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

GJ^hEuX%seKV( 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

9O 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

kp%+Jd0o^#vHAYiNrz~ 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 amq4@e3|ls{$AJ`A&-15-PbwbUhW%fwHnepB diff --git a/saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md b/saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md deleted file mode 100644 index 8643c31..0000000 --- a/saveDocs/software-quality-assurance/regression-test-suite-for-vertexcfd.md +++ /dev/null @@ -1,104 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) -> [Software quality assurance](software-quality-assurance.md) - -# VertexCFD Regression Testing - -The regression test logic is located in `VertexCFD/regression_test` and is run on the [CI](https://code-int.ornl.gov/vertex/vertexcfd/-/pipelines) using the script [`.gitlab-ci.yml`](../../.gitlab-ci.yml). The CI is currently set to run all tests on a Anduril node (128 CPUs) every time a new commit is pushed to a branch. The numerical solution generated by VertexCFD is compared to the gold files using the Trilinos tool [`exodiff`](https://gsjaardema.github.io/seacas-docs/exo_util.pdf). [`Pytest`](https://docs.pytest.org/en/6.2.x/contents.html) is being used as the main driver. - -The `VertexCFD/regression_test` directory contains the following items: -- a Python script `vertexcfd_test.py` that implements logic to set up each working directory, to run VertexCFD and to assert numerical solution against gold file using `exodiff`, -- a set of test directories that contains a Python script, a gold directory with Exodus files, and an `exodiff_file.txt` file. The Python script implements the test class with one function per test to run. Each function calls a specific input file and can support different options to pass the VertexCFD executable and the `exodiff` executable ([as a command line or from `exodiff_file.txt` file](https://gsjaardema.github.io/seacas-docs/exo_util.pdf)). The gold directory contains the VertexCFD output files that serve as gold (reference) files. - -## Adding new regression tests - -Adding a regression test in VertexCFD should be done with the following steps: - -1. Inside `VertexCFD/regression_test` directory, duplicate an existing test directory and rename it as `test_new_name`. - -2. Enter the new test directory `test_new_name` and rename the python script as `test_new_name.py`. Then, update the logic of the python script by changing the class name `class TestNewClassName`. - -3. Create a function for each test to add to the regression test suite: - ```python - def test_new_test_name(self): - #### Parameters to edit for each function #### - # Parameters for VertexCFD run - self.input_file = "XML_NAME.xml" - vertexcfd_options = "--kokkos-threads=1" - - # Parameters for exodiff executables - exodiff_options = "-steps last" - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options) - ``` - The input file for the test `test_new_test_name` is set by the variable `self.input_file`. Options to be passed to the VertexCFD and Exodiff executables are set by the variables `vertexcfd_options` and `exodiff_options`, respectively. - -4. Add the gold file to `test_new_name/gold` for the test implemented in function `test_new_test_name` and make sure that the name of the gold file matches the name of the output file set in the XML file `self.input_file`. - -5. Add the restart files (`.data` and `.dofmap`) to `test_new_name/restart` if needed. The names of the restart file should be of the form `base_input_file_read.data` and `base_input_file_read.dofmap` where `base_input_file` corresponds to the name of the input file `self.input_file` without the the format `.xml`. Please refer to `regression_test/test_tp3/` for an example of how to use restart files in regression tests. - -6. Add a marker if needed based on the list of markers implemented in `regression_test/pytest.in`: - ```python - @pytest.mark.MARKER_NAME - def test_new_test_name(self): - ``` - -7. The last step consist of updating the list of XML file and Exodus mesh files to be copied to the build directory in `examples/CMakeLists.txt`, and update the below section `List of regression tests` with a description of the added tests. - -Once all 7 steps are completed, the new test should be ready to run on the CI after committing and pushing all changes. The regression tests can be run locally by using the command line on `anduril-login02` or `narsil-login2` within the `VertexCFD` directory of the current branch: `gitlab-runner exec shell --env 'MARKERS=' --env 'TEST_NAMES=regression-test`. The flag `--env` exports the variables `MARKERS` and `TEST_NAMES` to the CI environment. The `MARKERS` variable should be set to `push`, `daily`, `weekly` or `all` (see `pytest.ini` for a complete list of markers). The `TEST_NAMES` variables is used to select regression tests by names. Examples of how to use the `gitlab-runner` at the command line are provided below: -- `gitlab-runner exec shell --env 'MARKERS=push' --env 'TEST_NAMES=workbench' regression-test` will run all tests with marker `push` and whom names contain `workbench`. -- `gitlab-runner exec shell --env 'MARKERS=push' --env 'TEST_NAMES=workbench or simple_box' regression-test` will run all tests with marker `push` and whom names contain `workbench` or `simple_box`. -- `gitlab-runner exec shell --env 'MARKERS=push or serial' --env 'TEST_NAMES=simple_box' regression-test` will run all tests with markers `push` or `serial` and whom names contain `simple_box`. -- `gitlab-runner exec shell --env 'TEST_NAMES=simple_box' regression-test` will run all tests whom names contain `simple_box`. -- `gitlab-runner exec shell --env 'MARKERS=push or serial' regression-test` will run all tests with marker `push` or `serial`. - -NOTE: in the current implementation, each test runs in less than 1 minute on one Anduril node. - -## Failed regression tests - -Regression tests that fail are automatically moved in the directory `vertexcfd/regression_failures`. If run on the CI, the directory `regression_failures` is available as an artifact for further analysis and is accessible by clicking on `Browse`, `build` and `regression_failures` from the pipeline. - -The directory `regression_failures` contains one sub-directory per class with failed regression tests. Each sub-directory contains the corresponding input file, the solution file, the command file `exodiff_file.txt`, and a `diff` Exodus file between the numerical solution and the gold solution. The `diff` file is generated from `exodiff` executable. - -For illustration purpose, let's assume that three input files from three different classes were modified: -- `input_file_1.xml`: the number of elements in the X direction is modified. VertexCFD will run but `exodiff` call will fail since the number of degrees of freedom will be different between the solution file and the gold file. -- `input_file_2.xml`: the XML format is invalid (missing `>` for instance) which will cause VertexCFD to fail when parsing the input file. Consequently, VertexCFD will not run and the solution file will not be generated. -- `input_file_3.xml`: the value of a parameter was changed. It will cause the numerical solution to be different of the gold file and a `diff` file will be generated. - -The directory `regression_failures` will contain three sub-directories with input files, Exodus files and/or command files `exodiff_file.txt` as follows: -- `test_class_1`: - - `exodiff_file.txt` - - `input_file_1.xml` - - `input_file_1_solution.exo` -- `test_class_2`: - - `input_file_2.xml` -- `test_class_3`: - - `exodiff_file.txt` - - `input_file_3.xml` - - `input_file_3_solution.exo` - - `input_file_3_solution_diff.exo` - -## Renaming variables in gold Exodus files - -Under the assumption a variable name is changed in the source code, gold files have to be updated. One option is to re-run each regression test which is time consuming and not feasible within a reasonable amount of time. Another option consists of using the capabilities available in the Python wrapper `exodus3.py` of some of the Exodus libraries (Python 3 version). The Python script `rename_variable_exodus.py` was designed to replace names of variables without modifying their values with the following features: -- nodal variables and element variables can be updated. -- multiple variables can be passed to the script as a list. -- the Exodus files to update can be specified with a list of files (relative to the script). -- search and replace logic only works with exact match. If a variable name passed to the script is not found in the Exodus file, a warning message will be thrown. -- once the script completes, a full report is shown with variable names that were updated/not updated for each file. - -A few examples are presented to illustrate its usage: -- Replace nodal variable names in a Exodus file: -``` -python3 rename_variables_exodus.py -n velocity_0 velocity_0_new -n velocity_1 velocity_1_new test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_solution.exo -``` -- Replace an element variable name in files passed by using shell globbing: -``` -python3 rename_variables_exodus.py -e lagrange_pressure lagrange_pressure_new test_incompressible/test_pipe_flow/gold/incompressible_2d_*.exo -``` -- Replace a nodal variable name from a list of files specified at the command line: -``` -python3 rename_variables_exodus.py -n velocity_0 velocity_0_new test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_solution.exo test_incompressible/test_pipe_flow/gold/incompressible_2d_heated_channel_solution.exo -``` - -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) diff --git a/saveDocs/software-quality-assurance/software-quality-assurance.md b/saveDocs/software-quality-assurance/software-quality-assurance.md deleted file mode 100644 index b03b480..0000000 --- a/saveDocs/software-quality-assurance/software-quality-assurance.md +++ /dev/null @@ -1,5 +0,0 @@ -[Home page](https://code-int.ornl.gov/vertex/vertex-cfd/-/wikis/home) - -1. Unit testing - -2. [Regression testing](regression-test-suite-for-vertexcfd.md) From c741557e64d5724957fb7b72dc24332077313975 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 11:38:23 -0500 Subject: [PATCH 110/214] upgrade --- index.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.md b/index.md index 41e4013..e800587 100644 --- a/index.md +++ b/index.md @@ -38,5 +38,3 @@ If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBE url = {DOI_URL} } ``` - -[VERTEX-CFD repo]: https://github.com/ORNL/VERTEX-CFD From 34f3e87db9d8063c4bc64510c4e58f2c1f189a84 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 11:40:28 -0500 Subject: [PATCH 111/214] upgrade --- CMakeLists.txt | 234 --- examples/CMakeLists.txt | 71 - examples/README.md | 3 - .../full_induction_mhd/current_sheet_2d.xml | 383 ----- .../divergence_advection_2d.xml | 299 ---- .../full_induction_vortex_2d_pb.xml | 291 ---- .../full_induction_vortex_2d_pb_cuda.xml | 285 ---- .../full_induction_mhd/ldc_2d_bx_010.xml | 388 ----- .../ldc_2d_mixed_b_050_rotated.xml | 441 ------ ...incompressible_2d_backward_facing_step.xml | 280 ---- .../incompressible_2d_channel.xml | 280 ---- .../incompressible_2d_channel_periodic.xml | 269 ---- ...ible_2d_concentric_cylinder_convection.xml | 274 ---- .../incompressible_2d_heated_channel.xml | 310 ---- .../incompressible_2d_planar_poiseuille.xml | 311 ---- ...compressible_2d_planar_poiseuille_cuda.xml | 313 ---- ...ealizable_k_epsilon_turbulence_channel.xml | 320 ---- ...pressible_2d_rotating_cylinder_viscous.xml | 289 ---- ...2d_spalart_allmaras_turbulence_channel.xml | 314 ---- ...art_allmaras_turbulence_heated_channel.xml | 366 ----- ...e_2d_spalart_allmaras_turbulence_model.xml | 298 ---- ..._standard_k_epsilon_turbulence_channel.xml | 316 ---- .../incompressible_2d_taylor_green_vortex.xml | 256 ---- .../incompressible_2d_tee_junction.xml | 317 ---- .../incompressible_3d_channel_periodic.xml | 257 ---- .../incompressible_3d_wale_cavity.xml | 358 ----- ...mpressible_blunt_plate_laminar_flow_2d.xml | 277 ---- ...ble_oscillating_heated_laminar_flow_2d.xml | 351 ----- ...mpressible_oscillating_laminar_flow_2d.xml | 290 ---- ...mhd_2d_hartmann_pb_periodic_insulating.xml | 348 ----- ...d_hartmann_pb_periodic_insulating_cuda.xml | 344 ----- examples/inputs/simple_box_2d.xml | 269 ---- examples/inputs/simple_box_3d.xml | 300 ---- .../2d_backward_facing_step.exo | 3 - .../2d_concentric_convection.exo | 3 - .../2d_concentric_cylinders_rad10.exo | 3 - .../2d_concentric_cylinders_rad20.exo | 3 - .../2d_concentric_cylinders_rad40.exo | 3 - .../2d_concentric_cylinders_rad80.exo | 3 - .../2d_cyclinder_vertex_quad.exo | 3 - .../mesh/incompressible/2d_tee_junction.exo | 3 - .../mesh/incompressible/bluntplate_square.exo | 3 - .../half_turbulent_channel_mesh_one.exo | 3 - examples/mesh/incompressible/pipe_hex.exo | 3 - .../turbulent_channel_mesh_one.exo | 3 - examples/mesh/test_mesh_manager.exo | 3 - examples/post_processing/lambda-2-contour.py | 85 -- src/CMakeLists.txt | 515 ------- .../VertexCFD_BCStrategy_BoundaryFluxBase.cpp | 7 - .../VertexCFD_BCStrategy_BoundaryFluxBase.hpp | 128 -- ...exCFD_BCStrategy_BoundaryFluxBase_impl.hpp | 347 ----- .../VertexCFD_BCStrategy_Factory.hpp | 87 -- ..._BCStrategy_IncompressibleBoundaryFlux.cpp | 7 - ..._BCStrategy_IncompressibleBoundaryFlux.hpp | 82 -- ...rategy_IncompressibleBoundaryFlux_impl.hpp | 605 -------- ...ertexCFD_BCStrategy_StrongDirichletMMS.hpp | 49 - ...CFD_BCStrategy_StrongDirichletMMS_impl.hpp | 60 - ...undaryState_MethodManufacturedSolution.cpp | 7 - ...undaryState_MethodManufacturedSolution.hpp | 78 - ...yState_MethodManufacturedSolution_impl.hpp | 249 ---- ...ertexCFD_BoundaryState_ViscousGradient.cpp | 7 - ...ertexCFD_BoundaryState_ViscousGradient.hpp | 54 - ...CFD_BoundaryState_ViscousGradient_impl.hpp | 77 - ..._BoundaryState_ViscousPenaltyParameter.cpp | 7 - ..._BoundaryState_ViscousPenaltyParameter.hpp | 61 - ...daryState_ViscousPenaltyParameter_impl.hpp | 108 -- ..._Integrator_BoundaryGradBasisDotVector.cpp | 7 - ..._Integrator_BoundaryGradBasisDotVector.hpp | 103 -- ...grator_BoundaryGradBasisDotVector_impl.hpp | 429 ------ .../unit_test/CMakeLists.txt | 10 - .../tstMethodManufacturedSolutionBC.cpp | 350 ----- .../unit_test/tstViscousGradient.cpp | 169 --- .../unit_test/tstViscousPenaltyParameter.cpp | 129 -- .../VertexCFD_ClosureModelFactory.cpp | 7 - .../VertexCFD_ClosureModelFactory.hpp | 33 - ...ertexCFD_ClosureModelFactory_Hessian2d.cpp | 4 - ...ertexCFD_ClosureModelFactory_Hessian3d.cpp | 4 - ...rtexCFD_ClosureModelFactory_Jacobian2d.cpp | 4 - ...rtexCFD_ClosureModelFactory_Jacobian3d.cpp | 4 - ...rtexCFD_ClosureModelFactory_Residual2d.cpp | 4 - ...rtexCFD_ClosureModelFactory_Residual3d.cpp | 4 - ...ertexCFD_ClosureModelFactory_Tangent2d.cpp | 4 - ...ertexCFD_ClosureModelFactory_Tangent3d.cpp | 4 - ...FD_ClosureModelFactory_TemplateBuilder.hpp | 33 - .../VertexCFD_ClosureModelFactory_impl.hpp | 297 ---- .../VertexCFD_Closure_ConstantScalarField.cpp | 7 - .../VertexCFD_Closure_ConstantScalarField.hpp | 51 - ...exCFD_Closure_ConstantScalarField_impl.hpp | 54 - .../VertexCFD_Closure_ElementLength.cpp | 7 - .../VertexCFD_Closure_ElementLength.hpp | 50 - .../VertexCFD_Closure_ElementLength_impl.hpp | 67 - .../VertexCFD_Closure_ExternalFields.cpp | 7 - .../VertexCFD_Closure_ExternalFields.hpp | 69 - .../VertexCFD_Closure_ExternalFields_impl.hpp | 105 -- ...ertexCFD_Closure_ExternalMagneticField.cpp | 7 - ...ertexCFD_Closure_ExternalMagneticField.hpp | 67 - ...CFD_Closure_ExternalMagneticField_impl.hpp | 115 -- ...VertexCFD_Closure_MeasureElementLength.cpp | 7 - ...VertexCFD_Closure_MeasureElementLength.hpp | 52 - ...xCFD_Closure_MeasureElementLength_impl.hpp | 80 - ...CFD_Closure_MethodManufacturedSolution.cpp | 7 - ...CFD_Closure_MethodManufacturedSolution.hpp | 63 - ...osure_MethodManufacturedSolutionSource.hpp | 77 - ...thodManufacturedSolutionSource_Hessian.cpp | 9 - ...hodManufacturedSolutionSource_Jacobian.cpp | 9 - ...hodManufacturedSolutionSource_Residual.cpp | 9 - ...thodManufacturedSolutionSource_Tangent.cpp | 9 - ..._MethodManufacturedSolutionSource_impl.hpp | 307 ---- ...losure_MethodManufacturedSolution_impl.hpp | 155 -- .../VertexCFD_Closure_MetricTensor.cpp | 7 - .../VertexCFD_Closure_MetricTensor.hpp | 59 - ...xCFD_Closure_MetricTensorElementLength.cpp | 7 - ...xCFD_Closure_MetricTensorElementLength.hpp | 48 - ...Closure_MetricTensorElementLength_impl.hpp | 60 - .../VertexCFD_Closure_MetricTensor_impl.hpp | 206 --- ...CFD_Closure_SingularValueElementLength.cpp | 7 - ...CFD_Closure_SingularValueElementLength.hpp | 62 - ...losure_SingularValueElementLength_impl.hpp | 128 -- ...ertexCFD_Closure_VectorFieldDivergence.cpp | 7 - ...ertexCFD_Closure_VectorFieldDivergence.hpp | 58 - ...CFD_Closure_VectorFieldDivergence_impl.hpp | 78 - .../VertexCFD_Closure_WallDistance.cpp | 7 - .../VertexCFD_Closure_WallDistance.hpp | 105 -- .../VertexCFD_Closure_WallDistance_impl.hpp | 184 --- src/closure_models/unit_test/CMakeLists.txt | 26 - ...rtexCFD_ClosureModelFactoryTestHarness.hpp | 130 -- .../MethodManufacuredSolution2D_reference.py | 41 - .../MethodManufacuredSolution3D_reference.py | 54 - ...odManufacuredSolutionSource2D_reference.nb | 858 ----------- ...odManufacuredSolutionSource3D_reference.nb | 1292 ----------------- .../unit_test/doc/external_magnetic_field.py | 10 - .../unit_test/doc/singularValue_reference.py | 34 - .../unit_test/tstClampField.cpp | 146 -- .../tstClosureModelFactoryTestHarness.cpp | 38 - .../unit_test/tstConstantScalarField.cpp | 59 - .../unit_test/tstElementLength.cpp | 89 -- .../unit_test/tstExternalFields.cpp | 127 -- .../unit_test/tstExternalMagneticField.cpp | 163 --- .../unit_test/tstMeasureElementLength.cpp | 104 -- .../tstMethodManufacturedSolution.cpp | 173 --- .../tstMethodManufacturedSolutionSource.cpp | 244 ---- .../unit_test/tstMetricTensor.cpp | 588 -------- .../tstMetricTensorElementLength.cpp | 157 -- .../tstSingularValueElementLength.cpp | 186 --- .../unit_test/tstVectorFieldDivergence.cpp | 203 --- .../unit_test/tstWallDistance.cpp | 222 --- .../VertexCFD_ExternalFieldsManager.hpp | 48 - .../VertexCFD_ExternalFieldsManager_impl.hpp | 84 -- .../VertexCFD_InitialConditionManager.cpp | 124 -- .../VertexCFD_InitialConditionManager.hpp | 48 - src/drivers/VertexCFD_MeshManager.cpp | 117 -- src/drivers/VertexCFD_MeshManager.hpp | 48 - src/drivers/VertexCFD_PhysicsManager.cpp | 333 ----- src/drivers/VertexCFD_PhysicsManager.hpp | 85 -- src/drivers/unit_test/CMakeLists.txt | 12 - ...VertexCFD_DriverUnitTestConfig.hpp.cmakein | 8 - .../unit_test/data/simple_box_2d_restart.xml | 287 ---- .../unit_test/data/simple_box_fim_3d.xml | 268 ---- .../unit_test/tstInitialConditionManager.cpp | 172 --- src/drivers/unit_test/tstMeshManager.cpp | 199 --- src/drivers/unit_test/tstPhysicsManager.cpp | 136 -- src/drivers/vertexcfd.cpp | 528 ------- .../VertexCFD_EquationSet_Factory.hpp | 59 - .../VertexCFD_EquationSet_Heat.cpp | 6 - .../VertexCFD_EquationSet_Heat.hpp | 50 - .../VertexCFD_EquationSet_Heat_impl.hpp | 147 -- ...EquationSet_IncompressibleNavierStokes.cpp | 7 - ...EquationSet_IncompressibleNavierStokes.hpp | 68 - ...ionSet_IncompressibleNavierStokes_impl.hpp | 471 ------ ..._BoundaryState_FullInductionConducting.cpp | 7 - ..._BoundaryState_FullInductionConducting.hpp | 87 -- ...daryState_FullInductionConducting_impl.hpp | 206 --- ...exCFD_BoundaryState_FullInductionFixed.cpp | 7 - ...exCFD_BoundaryState_FullInductionFixed.hpp | 69 - ..._BoundaryState_FullInductionFixed_impl.hpp | 134 -- ...CFD_FullInductionBoundaryState_Factory.cpp | 6 - ...CFD_FullInductionBoundaryState_Factory.hpp | 111 -- .../unit_test/CMakeLists.txt | 9 - .../doc/fullInductionConducting_reference.py | 88 -- .../unit_test/tstFullInductionConducting.cpp | 383 ----- .../unit_test/tstFullInductionFixed.cpp | 256 ---- ...exCFD_Closure_DivergenceCleaningSource.cpp | 7 - ...exCFD_Closure_DivergenceCleaningSource.hpp | 54 - ..._Closure_DivergenceCleaningSource_impl.hpp | 71 - ...Closure_FullInductionLocalTimeStepSize.cpp | 7 - ...Closure_FullInductionLocalTimeStepSize.hpp | 71 - ...re_FullInductionLocalTimeStepSize_impl.hpp | 88 -- ...D_Closure_FullInductionModelErrorNorms.hpp | 67 - ...sure_FullInductionModelErrorNorms_impl.hpp | 94 -- .../VertexCFD_Closure_GodunovPowellSource.cpp | 7 - .../VertexCFD_Closure_GodunovPowellSource.hpp | 67 - ...exCFD_Closure_GodunovPowellSource_impl.hpp | 81 -- ...texCFD_Closure_InductionConstantSource.cpp | 7 - ...texCFD_Closure_InductionConstantSource.hpp | 53 - ...D_Closure_InductionConstantSource_impl.hpp | 66 - ...texCFD_Closure_InductionConvectiveFlux.cpp | 7 - ...texCFD_Closure_InductionConvectiveFlux.hpp | 76 - ...D_Closure_InductionConvectiveFlux_impl.hpp | 126 -- ...rtexCFD_Closure_InductionResistiveFlux.cpp | 7 - ...rtexCFD_Closure_InductionResistiveFlux.hpp | 74 - ...FD_Closure_InductionResistiveFlux_impl.hpp | 154 -- ...ertexCFD_Closure_MHDVortexProblemExact.cpp | 7 - ...ertexCFD_Closure_MHDVortexProblemExact.hpp | 66 - ...CFD_Closure_MHDVortexProblemExact_impl.hpp | 116 -- ...losure_MagneticCorrectionDampingSource.cpp | 7 - ...losure_MagneticCorrectionDampingSource.hpp | 56 - ...e_MagneticCorrectionDampingSource_impl.hpp | 59 - .../VertexCFD_Closure_MagneticPressure.cpp | 7 - .../VertexCFD_Closure_MagneticPressure.hpp | 56 - ...ertexCFD_Closure_MagneticPressure_impl.hpp | 69 - .../VertexCFD_Closure_TotalMagneticField.cpp | 7 - .../VertexCFD_Closure_TotalMagneticField.hpp | 59 - ...CFD_Closure_TotalMagneticFieldGradient.cpp | 7 - ...CFD_Closure_TotalMagneticFieldGradient.hpp | 65 - ...losure_TotalMagneticFieldGradient_impl.hpp | 106 -- ...texCFD_Closure_TotalMagneticField_impl.hpp | 77 - ...exCFD_FullInductionClosureModelFactory.hpp | 38 - ...InductionClosureModelFactory_Hessian2d.cpp | 5 - ...InductionClosureModelFactory_Hessian3d.cpp | 5 - ...nductionClosureModelFactory_Jacobian2d.cpp | 5 - ...nductionClosureModelFactory_Jacobian3d.cpp | 5 - ...nductionClosureModelFactory_Residual2d.cpp | 5 - ...nductionClosureModelFactory_Residual3d.cpp | 5 - ...InductionClosureModelFactory_Tangent2d.cpp | 5 - ...InductionClosureModelFactory_Tangent3d.cpp | 5 - ..._FullInductionClosureModelFactory_impl.hpp | 231 --- .../closure_models/unit_test/CMakeLists.txt | 21 - .../doc/divergenceCleaningSource_reference.py | 36 - .../unit_test/doc/error_norms_reference.py | 17 - ...ullInductionLocalTimeStepSize_reference.py | 39 - .../doc/godunovPowellSource_reference.py | 42 - .../doc/inductionConvectiveFlux_reference.py | 80 - .../doc/inductionResitiveFlux_reference.py | 71 - .../doc/mhd_vortex_problem_exact_reference.py | 36 - .../unit_test/tstDivergenceCleaningSource.cpp | 188 --- .../unit_test/tstFullInductionErrorNorms.cpp | 196 --- .../tstFullInductionLocalTimeStepSize.cpp | 221 --- .../tstFullInductionTimeDerivative.cpp | 91 -- .../unit_test/tstGodunovPowellSource.cpp | 223 --- .../unit_test/tstInductionConstantSource.cpp | 127 -- .../unit_test/tstInductionConvectiveFlux.cpp | 313 ---- .../unit_test/tstInductionResistiveFlux.cpp | 488 ------- .../unit_test/tstMHDVortexProblemExact.cpp | 162 --- .../tstMagneticCorrectionDampingSource.cpp | 157 -- .../unit_test/tstMagneticPressure.cpp | 156 -- .../unit_test/tstResistivityFactory.cpp | 95 -- .../unit_test/tstTotalMagneticField.cpp | 214 --- .../tstTotalMagneticFieldGradient.cpp | 243 ---- ...D_FullInductionInitialConditionFactory.cpp | 7 - ...D_FullInductionInitialConditionFactory.hpp | 40 - ...lInductionInitialConditionFactory_impl.hpp | 59 - ...itialCondition_DivergenceAdvectionTest.cpp | 7 - ...itialCondition_DivergenceAdvectionTest.hpp | 65 - ...Condition_DivergenceAdvectionTest_impl.hpp | 156 -- ...xCFD_InitialCondition_MHDVortexProblem.cpp | 7 - ...xCFD_InitialCondition_MHDVortexProblem.hpp | 63 - ...InitialCondition_MHDVortexProblem_impl.hpp | 112 -- .../unit_test/CMakeLists.txt | 9 - .../unit_test/tstDivergenceAdvectionTest.cpp | 251 ---- .../unit_test/tstMHDVortexProblem.cpp | 192 --- .../VertexCFD_FullInductionMHDProperties.hpp | 140 -- .../mhd_properties/unit_test/CMakeLists.txt | 8 - .../tstFullInductionMHDProperties.cpp | 151 -- ...sure_IncompressibleLSVOFConvectiveFlux.cpp | 7 - ...sure_IncompressibleLSVOFConvectiveFlux.hpp | 62 - ...IncompressibleLSVOFConvectiveFlux_impl.hpp | 86 -- .../closure_models/unit_test/CMakeLists.txt | 9 - .../tstIncompressibleLSVOFConvectiveFlux.cpp | 165 --- ..._BoundaryState_IncompressibleCavityLid.cpp | 7 - ..._BoundaryState_IncompressibleCavityLid.hpp | 97 -- ...daryState_IncompressibleCavityLid_impl.hpp | 190 --- ..._BoundaryState_IncompressibleDirichlet.cpp | 7 - ..._BoundaryState_IncompressibleDirichlet.hpp | 85 -- ...daryState_IncompressibleDirichlet_impl.hpp | 156 -- ...D_BoundaryState_IncompressibleFreeSlip.cpp | 7 - ...D_BoundaryState_IncompressibleFreeSlip.hpp | 86 -- ...ndaryState_IncompressibleFreeSlip_impl.hpp | 141 -- ...oundaryState_IncompressibleLaminarFlow.cpp | 7 - ...oundaryState_IncompressibleLaminarFlow.hpp | 103 -- ...ryState_IncompressibleLaminarFlow_impl.hpp | 154 -- ...CFD_BoundaryState_IncompressibleNoSlip.cpp | 7 - ...CFD_BoundaryState_IncompressibleNoSlip.hpp | 82 -- ...oundaryState_IncompressibleNoSlip_impl.hpp | 123 -- ...aryState_IncompressiblePressureOutflow.cpp | 7 - ...aryState_IncompressiblePressureOutflow.hpp | 95 -- ...ate_IncompressiblePressureOutflow_impl.hpp | 132 -- ...undaryState_IncompressibleRotatingWall.cpp | 7 - ...undaryState_IncompressibleRotatingWall.hpp | 99 -- ...yState_IncompressibleRotatingWall_impl.hpp | 178 --- ...D_BoundaryState_IncompressibleSymmetry.cpp | 7 - ...D_BoundaryState_IncompressibleSymmetry.hpp | 86 -- ...ndaryState_IncompressibleSymmetry_impl.hpp | 157 -- ...undaryState_IncompressibleWallFunction.cpp | 7 - ...undaryState_IncompressibleWallFunction.hpp | 92 -- ...yState_IncompressibleWallFunction_impl.hpp | 171 --- ...FD_IncompressibleBoundaryState_Factory.cpp | 6 - ...FD_IncompressibleBoundaryState_Factory.hpp | 171 --- .../unit_test/CMakeLists.txt | 16 - .../incompressible_cavity_flow_reference.py | 34 - .../incompressible_laminar_flow_reference.py | 41 - .../doc/incompressible_symmetry_reference.py | 27 - .../incompressible_wall_function_reference.py | 51 - ..._incompressible_rotating_wall_reference.py | 99 -- .../tstIncompressibleBoundaryLaminarFlow.cpp | 363 ----- .../unit_test/tstIncompressibleCavityLid.cpp | 525 ------- .../unit_test/tstIncompressibleFreeSlip.cpp | 409 ------ .../unit_test/tstIncompressibleNoSlip.cpp | 370 ----- .../tstIncompressiblePressureOutflow.cpp | 359 ----- .../unit_test/tstIncompressibleSymmetry.cpp | 438 ------ .../tstIncompressibleWallFunction.cpp | 424 ------ ...stTimeTransientIncompressibleDirichlet.cpp | 404 ------ ...imeTransientIncompressibleRotatingWall.cpp | 449 ------ ...D_Closure_IncompressibleBuoyancySource.cpp | 7 - ...D_Closure_IncompressibleBuoyancySource.hpp | 65 - ...sure_IncompressibleBuoyancySource_impl.hpp | 88 -- ...D_Closure_IncompressibleConstantSource.cpp | 7 - ...D_Closure_IncompressibleConstantSource.hpp | 62 - ...sure_IncompressibleConstantSource_impl.hpp | 85 -- ...D_Closure_IncompressibleConvectiveFlux.cpp | 7 - ...D_Closure_IncompressibleConvectiveFlux.hpp | 72 - ...sure_IncompressibleConvectiveFlux_impl.hpp | 105 -- ...exCFD_Closure_IncompressibleErrorNorms.hpp | 78 - ..._Closure_IncompressibleErrorNorms_impl.hpp | 138 -- ...rtexCFD_Closure_IncompressibleLiftDrag.cpp | 7 - ...rtexCFD_Closure_IncompressibleLiftDrag.hpp | 76 - ...FD_Closure_IncompressibleLiftDrag_impl.hpp | 104 -- ...losure_IncompressibleLocalTimeStepSize.cpp | 7 - ...losure_IncompressibleLocalTimeStepSize.hpp | 57 - ...e_IncompressibleLocalTimeStepSize_impl.hpp | 70 - ...re_IncompressiblePlanarPoiseuilleExact.cpp | 7 - ...re_IncompressiblePlanarPoiseuilleExact.hpp | 74 - ...compressiblePlanarPoiseuilleExact_impl.hpp | 117 -- ...ure_IncompressibleRotatingAnnulusExact.cpp | 7 - ...ure_IncompressibleRotatingAnnulusExact.hpp | 76 - ...ncompressibleRotatingAnnulusExact_impl.hpp | 124 -- ...D_Closure_IncompressibleShearVariables.cpp | 7 - ...D_Closure_IncompressibleShearVariables.hpp | 64 - ...sure_IncompressibleShearVariables_impl.hpp | 90 -- ...ressibleTaylorGreenVortexExactSolution.cpp | 7 - ...ressibleTaylorGreenVortexExactSolution.hpp | 66 - ...bleTaylorGreenVortexExactSolution_impl.hpp | 91 -- ...D_Closure_IncompressibleTimeDerivative.cpp | 7 - ...D_Closure_IncompressibleTimeDerivative.hpp | 66 - ...sure_IncompressibleTimeDerivative_impl.hpp | 88 -- ...e_IncompressibleVariableTimeDerivative.cpp | 7 - ...e_IncompressibleVariableTimeDerivative.hpp | 54 - ...ompressibleVariableTimeDerivative_impl.hpp | 64 - ...xCFD_Closure_IncompressibleViscousFlux.cpp | 7 - ...xCFD_Closure_IncompressibleViscousFlux.hpp | 87 -- ...Closure_IncompressibleViscousFlux_impl.hpp | 147 -- ...xCFD_Closure_IncompressibleViscousHeat.cpp | 7 - ...xCFD_Closure_IncompressibleViscousHeat.hpp | 69 - ...Closure_IncompressibleViscousHeat_impl.hpp | 96 -- ...xCFD_IncompressibleClosureModelFactory.hpp | 37 - ...pressibleClosureModelFactory_Hessian2d.cpp | 5 - ...pressibleClosureModelFactory_Hessian3d.cpp | 5 - ...ressibleClosureModelFactory_Jacobian2d.cpp | 5 - ...ressibleClosureModelFactory_Jacobian3d.cpp | 5 - ...ressibleClosureModelFactory_Residual2d.cpp | 5 - ...ressibleClosureModelFactory_Residual3d.cpp | 5 - ...pressibleClosureModelFactory_Tangent2d.cpp | 5 - ...pressibleClosureModelFactory_Tangent3d.cpp | 5 - ...IncompressibleClosureModelFactory_impl.hpp | 212 --- .../closure_models/unit_test/CMakeLists.txt | 21 - .../doc/convective_flux_reference.py | 42 - .../unit_test/doc/error_norms_reference.py | 17 - .../unit_test/doc/liftdrag_reference.py | 67 - .../doc/local_time_step_size_reference.py | 15 - .../doc/planar_poiseuille_reference.py | 35 - .../doc/rotating_annulus_exact_reference.py | 40 - .../doc/shear_variables_reference.py | 29 - .../doc/taylor_green_vortex_reference.py | 32 - .../unit_test/doc/viscous_flux_reference.py | 87 -- .../unit_test/doc/viscous_heat_reference.py | 28 - .../tstIncompressibleBuoyancySource.cpp | 197 --- .../tstIncompressibleConstantSource.cpp | 165 --- .../tstIncompressibleConvectiveFlux.cpp | 274 ---- .../unit_test/tstIncompressibleErrorNorms.cpp | 260 ---- .../unit_test/tstIncompressibleLiftDrag.cpp | 372 ----- .../tstIncompressibleLocalTimeStepSize.cpp | 190 --- ...tstIncompressiblePlanarPoiseuilleExact.cpp | 164 --- .../tstIncompressibleRotatingAnnulusExact.cpp | 163 --- .../tstIncompressibleShearVariables.cpp | 187 --- ...ressibleTaylorGreenVortexExactSolution.cpp | 132 -- .../tstIncompressibleTimeDerivative.cpp | 272 ---- ...stIncompressibleVariableTimeDerivative.cpp | 91 -- .../tstIncompressibleViscousFlux.cpp | 602 -------- .../tstIncompressibleViscousHeat.cpp | 199 --- .../VertexCFD_ConstantFluidProperties.hpp | 159 -- .../fluid_properties/unit_test/CMakeLists.txt | 8 - ...tIncompressibleConstantFluidProperties.cpp | 157 -- ...ialCondition_IncompressibleLaminarFlow.cpp | 7 - ...ialCondition_IncompressibleLaminarFlow.hpp | 73 - ...ndition_IncompressibleLaminarFlow_impl.hpp | 106 -- ...dition_IncompressibleTaylorGreenVortex.cpp | 7 - ...dition_IncompressibleTaylorGreenVortex.hpp | 60 - ...n_IncompressibleTaylorGreenVortex_impl.hpp | 87 -- ...ialCondition_IncompressibleVortexInBox.cpp | 7 - ...ialCondition_IncompressibleVortexInBox.hpp | 58 - ...ndition_IncompressibleVortexInBox_impl.hpp | 87 -- .../unit_test/CMakeLists.txt | 10 - .../incompressible_laminar_flow_reference.py | 56 - ...pressible_taylor_green_vortex_reference.py | 29 - .../incompressible_vortex_box_reference.py | 30 - .../tstIncompressibleLaminarFlow.cpp | 166 --- .../tstIncompressibleTaylorGreenVortex.cpp | 103 -- .../tstIncompressibleVortexInBox.cpp | 99 -- ...D_BoundaryState_ElectricPotentialFixed.cpp | 7 - ...D_BoundaryState_ElectricPotentialFixed.hpp | 59 - ...ndaryState_ElectricPotentialFixed_impl.hpp | 96 -- ...yState_ElectricPotentialInsulatingWall.cpp | 7 - ...yState_ElectricPotentialInsulatingWall.hpp | 55 - ...e_ElectricPotentialInsulatingWall_impl.hpp | 78 - ...ElectricPotentialBoundaryState_Factory.cpp | 6 - ...ElectricPotentialBoundaryState_Factory.hpp | 69 - .../unit_test/CMakeLists.txt | 9 - .../doc/insulating_wall_reference.py | 23 - .../tstElectricPotentialInsulatingWall.cpp | 149 -- ...tstTimeTransientElectricPotentialFixed.cpp | 276 ---- ...rtexCFD_Closure_ElectricCurrentDensity.cpp | 7 - ...rtexCFD_Closure_ElectricCurrentDensity.hpp | 66 - ...FD_Closure_ElectricCurrentDensity_impl.hpp | 102 -- ...sure_ElectricPotentialCrossProductFlux.cpp | 7 - ...sure_ElectricPotentialCrossProductFlux.hpp | 69 - ...ElectricPotentialCrossProductFlux_impl.hpp | 95 -- ...Closure_ElectricPotentialDiffusionFlux.cpp | 7 - ...Closure_ElectricPotentialDiffusionFlux.hpp | 61 - ...re_ElectricPotentialDiffusionFlux_impl.hpp | 70 - ...VertexCFD_Closure_HartmannProblemExact.cpp | 7 - ...VertexCFD_Closure_HartmannProblemExact.hpp | 71 - ...xCFD_Closure_HartmannProblemExact_impl.hpp | 104 -- .../VertexCFD_Closure_LorentzForce.cpp | 7 - .../VertexCFD_Closure_LorentzForce.hpp | 67 - .../VertexCFD_Closure_LorentzForce_impl.hpp | 115 -- ...exCFD_InductionlessClosureModelFactory.hpp | 36 - ...ctionlessClosureModelFactory_Hessian2d.cpp | 5 - ...ctionlessClosureModelFactory_Hessian3d.cpp | 5 - ...tionlessClosureModelFactory_Jacobian2d.cpp | 5 - ...tionlessClosureModelFactory_Jacobian3d.cpp | 5 - ...tionlessClosureModelFactory_Residual2d.cpp | 5 - ...tionlessClosureModelFactory_Residual3d.cpp | 5 - ...ctionlessClosureModelFactory_Tangent2d.cpp | 5 - ...ctionlessClosureModelFactory_Tangent3d.cpp | 5 - ..._InductionlessClosureModelFactory_impl.hpp | 117 -- .../closure_models/unit_test/CMakeLists.txt | 12 - .../doc/elec_pot_equ_cross_product_flux.py | 18 - .../elec_pot_equ_current_density_reference.py | 20 - .../elec_pot_equ_lorentz_force_reference.py | 25 - .../doc/elec_pot_hartmann_problem.py | 18 - .../unit_test/tstElectricCurrentDensity.cpp | 211 --- .../tstElectricPotentialCrossProductFlux.cpp | 205 --- .../tstElectricPotentialDiffusionFlux.cpp | 168 --- .../unit_test/tstHartmannProblem.cpp | 139 -- .../unit_test/tstLorentzForce.cpp | 205 --- .../VertexCFD_InitialConditionFactory.cpp | 7 - .../VertexCFD_InitialConditionFactory.hpp | 39 - ...nitialConditionFactory_TemplateBuilder.hpp | 42 - ...VertexCFD_InitialConditionFactory_impl.hpp | 276 ---- .../VertexCFD_InitialCondition_Circle.cpp | 7 - .../VertexCFD_InitialCondition_Circle.hpp | 60 - ...VertexCFD_InitialCondition_Circle_impl.hpp | 88 -- .../VertexCFD_InitialCondition_Constant.cpp | 8 - .../VertexCFD_InitialCondition_Constant.hpp | 48 - ...rtexCFD_InitialCondition_Constant_impl.hpp | 44 - .../VertexCFD_InitialCondition_Gaussian.cpp | 7 - .../VertexCFD_InitialCondition_Gaussian.hpp | 61 - ...rtexCFD_InitialCondition_Gaussian_impl.hpp | 104 -- ...exCFD_InitialCondition_InverseGaussian.cpp | 7 - ...exCFD_InitialCondition_InverseGaussian.hpp | 61 - ..._InitialCondition_InverseGaussian_impl.hpp | 104 -- ...alCondition_MethodManufacturedSolution.cpp | 7 - ...alCondition_MethodManufacturedSolution.hpp | 63 - ...dition_MethodManufacturedSolution_impl.hpp | 159 -- .../VertexCFD_InitialCondition_Step.cpp | 7 - .../VertexCFD_InitialCondition_Step.hpp | 56 - .../VertexCFD_InitialCondition_Step_impl.hpp | 74 - .../unit_test/CMakeLists.txt | 12 - .../unit_test/doc/gaussian_reference.py | 122 -- .../unit_test/tstInitialConditionCircle.cpp | 103 -- .../unit_test/tstInitialConditionConstant.cpp | 63 - .../unit_test/tstInitialConditionGaussian.cpp | 190 --- .../unit_test/tstInitialConditionStep.cpp | 94 -- .../tstMethodManufacturedSolutionIC.cpp | 226 --- .../VertexCFD_LinearSolvers_CusolverGLU.cpp | 325 ----- .../VertexCFD_LinearSolvers_CusolverGLU.hpp | 101 -- ...texCFD_LinearSolvers_CusolverNonpublic.hpp | 116 -- ...exCFD_LinearSolvers_LOWSFactoryBuilder.cpp | 67 - ...exCFD_LinearSolvers_LOWSFactoryBuilder.hpp | 37 - ...texCFD_LinearSolvers_LocalDirectSolver.hpp | 43 - ...exCFD_LinearSolvers_LocalSolverFactory.cpp | 54 - ...exCFD_LinearSolvers_LocalSolverFactory.hpp | 31 - ...VertexCFD_LinearSolvers_Preconditioner.cpp | 138 -- ...VertexCFD_LinearSolvers_Preconditioner.hpp | 105 -- ...FD_LinearSolvers_PreconditionerFactory.cpp | 178 --- ...FD_LinearSolvers_PreconditionerFactory.hpp | 63 - .../VertexCFD_LinearSolvers_SuperLU.cpp | 236 --- .../VertexCFD_LinearSolvers_SuperLU.hpp | 73 - src/linear_solvers/unit_test/CMakeLists.txt | 16 - .../unit_test/VertexCFD_SolverTester.hpp | 146 -- .../unit_test/tstCusolverGLU.cpp | 52 - .../unit_test/tstLocalSolverFactory.cpp | 41 - .../unit_test/tstPreconditioner.cpp | 125 -- .../unit_test/tstPreconditionerFactory.cpp | 81 -- src/mesh/VertexCFD_Mesh_ExodusWriter.cpp | 119 -- src/mesh/VertexCFD_Mesh_ExodusWriter.hpp | 65 - src/mesh/VertexCFD_Mesh_GeometryData.hpp | 159 -- .../VertexCFD_Mesh_GeometryPrimitives.hpp | 384 ----- src/mesh/VertexCFD_Mesh_Restart.cpp | 717 --------- src/mesh/VertexCFD_Mesh_Restart.hpp | 91 -- src/mesh/VertexCFD_Mesh_StkReaderFactory.cpp | 505 ------- src/mesh/VertexCFD_Mesh_StkReaderFactory.hpp | 128 -- src/mesh/unit_test/CMakeLists.txt | 28 - src/mesh/unit_test/test_data/.gitattributes | 1 - .../test_data/read_only_test.restart.data | Bin 10832 -> 0 bytes .../test_data/read_only_test.restart.dofmap | Bin 25004 -> 0 bytes .../read_only_test_periodic.restart.data | Bin 10016 -> 0 bytes .../read_only_test_periodic.restart.dofmap | Bin 25004 -> 0 bytes src/mesh/unit_test/tstGeometryPrimitives.cpp | 316 ---- src/mesh/unit_test/tstRestart.cpp | 374 ----- .../VertexCFD_Compute_ErrorNorms.hpp | 100 -- .../VertexCFD_Compute_ErrorNorms_impl.hpp | 174 --- src/observers/VertexCFD_Compute_Volume.hpp | 71 - .../VertexCFD_Compute_Volume_impl.hpp | 100 -- .../VertexCFD_NOXObserver_IterationOutput.cpp | 86 -- .../VertexCFD_NOXObserver_IterationOutput.hpp | 58 - ...rtexCFD_TempusObserver_ErrorNormOutput.hpp | 89 -- ...FD_TempusObserver_ErrorNormOutput_impl.hpp | 202 --- ...rtexCFD_TempusObserver_IterationOutput.hpp | 76 - ...FD_TempusObserver_IterationOutput_impl.hpp | 153 -- ...ertexCFD_TempusObserver_ResponseOutput.hpp | 76 - ...CFD_TempusObserver_ResponseOutput_impl.hpp | 141 -- .../VertexCFD_TempusObserver_WriteMatrix.hpp | 92 -- ...texCFD_TempusObserver_WriteMatrix_impl.hpp | 345 ----- .../VertexCFD_TempusObserver_WriteRestart.hpp | 79 - ...exCFD_TempusObserver_WriteRestart_impl.hpp | 107 -- ...VertexCFD_TempusObserver_WriteToExodus.hpp | 79 - ...xCFD_TempusObserver_WriteToExodus_impl.hpp | 110 -- ...texCFD_TempusTimeStepControl_GlobalCFL.hpp | 60 - ...D_TempusTimeStepControl_GlobalCFL_impl.hpp | 146 -- ...D_TempusTimeStepControl_GlobalTimeStep.hpp | 50 - ...pusTimeStepControl_GlobalTimeStep_impl.hpp | 74 - ...rtexCFD_TempusTimeStepControl_Strategy.hpp | 33 - src/observers/unit_test/CDR_Model.hpp | 175 --- src/observers/unit_test/CDR_Model_impl.hpp | 637 -------- src/observers/unit_test/CMakeLists.txt | 9 - src/observers/unit_test/tstWriteMatrix.cpp | 272 ---- .../VertexCFD_GeneralScalarParameter.cpp | 7 - .../VertexCFD_GeneralScalarParameter.hpp | 45 - .../VertexCFD_GeneralScalarParameterInput.cpp | 32 - .../VertexCFD_GeneralScalarParameterInput.hpp | 45 - .../VertexCFD_GeneralScalarParameter_impl.hpp | 73 - .../VertexCFD_ParameterDatabase.cpp | 357 ----- .../VertexCFD_ParameterDatabase.hpp | 118 -- src/parameters/VertexCFD_ScalarParameter.cpp | 6 - src/parameters/VertexCFD_ScalarParameter.hpp | 38 - .../VertexCFD_ScalarParameterEvaluator.cpp | 7 - .../VertexCFD_ScalarParameterEvaluator.hpp | 45 - ...ertexCFD_ScalarParameterEvaluator_impl.hpp | 41 - .../VertexCFD_ScalarParameterInput.cpp | 30 - .../VertexCFD_ScalarParameterInput.hpp | 44 - .../VertexCFD_ScalarParameterManager.cpp | 7 - .../VertexCFD_ScalarParameterManager.hpp | 48 - .../VertexCFD_ScalarParameterManager_impl.hpp | 61 - .../VertexCFD_ScalarParameterObserver.cpp | 7 - .../VertexCFD_ScalarParameterObserver.hpp | 60 - ...VertexCFD_ScalarParameterObserver_impl.hpp | 97 -- .../VertexCFD_ScalarParameter_impl.hpp | 36 - src/parameters/unit_test/CMakeLists.txt | 18 - ...texCFD_ParameterUnitTestConfig.hpp.cmakein | 7 - .../unit_test/data/input_parser_test.xml | 71 - .../data/scalar_parameter_evaluator_test.xml | 111 -- .../unit_test/tstGeneralScalarParameter.cpp | 111 -- .../tstGeneralScalarParameterInput.cpp | 54 - .../unit_test/tstParameterDatabase.cpp | 175 --- .../unit_test/tstScalarParameter.cpp | 83 -- .../unit_test/tstScalarParameterEvaluator.cpp | 223 --- .../unit_test/tstScalarParameterInput.cpp | 55 - .../unit_test/tstScalarParameterObserver.cpp | 155 -- src/responses/VertexCFD_ResponseManager.cpp | 285 ---- src/responses/VertexCFD_ResponseManager.hpp | 91 -- src/responses/VertexCFD_Response_Utils.cpp | 52 - src/responses/VertexCFD_Response_Utils.hpp | 26 - src/responses/unit_test/CMakeLists.txt | 18 - ...rtexCFD_ResponseUnitTestConfig.hpp.cmakein | 8 - .../unit_test/data/response_manager_test.xml | 86 -- .../unit_test/tstResponseManager.cpp | 206 --- src/responses/unit_test/tstResponseUtils.cpp | 59 - src/test_harness/CMakeLists.txt | 2 - src/test_harness/TestHarness.cmake | 80 - .../VertexCFD_EvaluatorTestHarness.hpp | 367 ----- src/test_harness/test_main.cpp | 16 - src/test_harness/unit_test/CMakeLists.txt | 10 - .../unit_test/tstEvaluatorTestHarness.cpp | 495 ------- src/test_harness/unit_test/tstKokkosMPI.cpp | 47 - ...yState_TurbulenceBoundaryEddyViscosity.cpp | 7 - ...yState_TurbulenceBoundaryEddyViscosity.hpp | 55 - ...e_TurbulenceBoundaryEddyViscosity_impl.hpp | 87 -- ...FD_BoundaryState_TurbulenceExtrapolate.cpp | 7 - ...FD_BoundaryState_TurbulenceExtrapolate.hpp | 54 - ...undaryState_TurbulenceExtrapolate_impl.hpp | 72 - ...ertexCFD_BoundaryState_TurbulenceFixed.cpp | 7 - ...ertexCFD_BoundaryState_TurbulenceFixed.hpp | 55 - ...CFD_BoundaryState_TurbulenceFixed_impl.hpp | 72 - ...FD_BoundaryState_TurbulenceInletOutlet.cpp | 7 - ...FD_BoundaryState_TurbulenceInletOutlet.hpp | 61 - ...undaryState_TurbulenceInletOutlet_impl.hpp | 95 -- ...ryState_TurbulenceKEpsilonWallFunction.cpp | 7 - ...ryState_TurbulenceKEpsilonWallFunction.hpp | 84 -- ...te_TurbulenceKEpsilonWallFunction_impl.hpp | 200 --- ...exCFD_BoundaryState_TurbulenceSymmetry.cpp | 7 - ...exCFD_BoundaryState_TurbulenceSymmetry.hpp | 56 - ..._BoundaryState_TurbulenceSymmetry_impl.hpp | 83 -- ...texCFD_TurbulenceBoundaryState_Factory.cpp | 6 - ...texCFD_TurbulenceBoundaryState_Factory.hpp | 292 ---- .../unit_test/CMakeLists.txt | 13 - .../doc/KEpsilon_Wall_Function_reference.py | 62 - .../doc/turbulence_symmetry_reference.py | 21 - .../tstTurbulenceBoundaryEddyViscosity.cpp | 161 -- .../unit_test/tstTurbulenceExtrapolate.cpp | 123 -- .../unit_test/tstTurbulenceFixed.cpp | 122 -- .../unit_test/tstTurbulenceInletOutlet.cpp | 221 --- .../tstTurbulenceKEpsilonWallFunction.cpp | 341 ----- .../unit_test/tstTurbulenceSymmetry.cpp | 155 -- ...ressibleKEpsilonDiffusivityCoefficient.cpp | 7 - ...ressibleKEpsilonDiffusivityCoefficient.hpp | 63 - ...bleKEpsilonDiffusivityCoefficient_impl.hpp | 72 - ...re_IncompressibleKEpsilonEddyViscosity.cpp | 7 - ...re_IncompressibleKEpsilonEddyViscosity.hpp | 56 - ...compressibleKEpsilonEddyViscosity_impl.hpp | 69 - ...D_Closure_IncompressibleKEpsilonSource.cpp | 7 - ...D_Closure_IncompressibleKEpsilonSource.hpp | 68 - ...sure_IncompressibleKEpsilonSource_impl.hpp | 116 -- ...mpressibleKOmegaDiffusivityCoefficient.cpp | 7 - ...mpressibleKOmegaDiffusivityCoefficient.hpp | 63 - ...sibleKOmegaDiffusivityCoefficient_impl.hpp | 102 -- ...sure_IncompressibleKOmegaEddyViscosity.cpp | 7 - ...sure_IncompressibleKOmegaEddyViscosity.hpp | 62 - ...IncompressibleKOmegaEddyViscosity_impl.hpp | 93 -- ...CFD_Closure_IncompressibleKOmegaSource.cpp | 7 - ...CFD_Closure_IncompressibleKOmegaSource.hpp | 78 - ...losure_IncompressibleKOmegaSource_impl.hpp | 224 --- ...essibleRealizableKEpsilonEddyViscosity.cpp | 7 - ...essibleRealizableKEpsilonEddyViscosity.hpp | 62 - ...leRealizableKEpsilonEddyViscosity_impl.hpp | 131 -- ...IncompressibleRealizableKEpsilonSource.cpp | 7 - ...IncompressibleRealizableKEpsilonSource.hpp | 72 - ...pressibleRealizableKEpsilonSource_impl.hpp | 133 -- ...eSpalartAllmarasDiffusivityCoefficient.cpp | 7 - ...eSpalartAllmarasDiffusivityCoefficient.hpp | 62 - ...artAllmarasDiffusivityCoefficient_impl.hpp | 72 - ...mpressibleSpalartAllmarasEddyViscosity.cpp | 7 - ...mpressibleSpalartAllmarasEddyViscosity.hpp | 60 - ...sibleSpalartAllmarasEddyViscosity_impl.hpp | 76 - ...re_IncompressibleSpalartAllmarasSource.cpp | 7 - ...re_IncompressibleSpalartAllmarasSource.hpp | 80 - ...compressibleSpalartAllmarasSource_impl.hpp | 190 --- ...e_IncompressibleVariableConvectiveFlux.cpp | 7 - ...e_IncompressibleVariableConvectiveFlux.hpp | 63 - ...ompressibleVariableConvectiveFlux_impl.hpp | 74 - ...re_IncompressibleVariableDiffusionFlux.cpp | 7 - ...re_IncompressibleVariableDiffusionFlux.hpp | 63 - ...compressibleVariableDiffusionFlux_impl.hpp | 74 - ...losure_IncompressibleWALEEddyViscosity.cpp | 7 - ...losure_IncompressibleWALEEddyViscosity.hpp | 62 - ...e_IncompressibleWALEEddyViscosity_impl.hpp | 166 --- ...ertexCFD_TurbulenceClosureModelFactory.hpp | 35 - ...urbulenceClosureModelFactory_Hessian2d.cpp | 5 - ...urbulenceClosureModelFactory_Hessian3d.cpp | 5 - ...rbulenceClosureModelFactory_Jacobian2d.cpp | 5 - ...rbulenceClosureModelFactory_Jacobian3d.cpp | 5 - ...rbulenceClosureModelFactory_Residual2d.cpp | 5 - ...rbulenceClosureModelFactory_Residual3d.cpp | 5 - ...urbulenceClosureModelFactory_Tangent2d.cpp | 5 - ...urbulenceClosureModelFactory_Tangent3d.cpp | 5 - ...CFD_TurbulenceClosureModelFactory_impl.hpp | 255 ---- .../closure_models/unit_test/CMakeLists.txt | 21 - .../unit_test/doc/KEpsilonSource_reference.py | 50 - .../doc/KOmegaEddyViscosity_reference.py | 48 - .../unit_test/doc/KOmegaSource_reference.py | 96 -- ...alizableKEpsilonEddyViscosity_reference.py | 60 - .../doc/RealizableKEpsilonSource_reference.py | 63 - .../doc/SADiffusivityCoefficient_reference.py | 19 - .../doc/SAEddyViscosity_reference.py | 16 - .../unit_test/doc/SASource_reference.py | 102 -- .../doc/WALEEddyViscosity_reference.py | 53 - ...ressibleKEpsilonDiffusivityCoefficient.cpp | 108 -- ...tstIncompressibleKEpsilonEddyViscosity.cpp | 100 -- .../tstIncompressibleKEpsilonSource.cpp | 156 -- ...mpressibleKOmegaDiffusivityCoefficient.cpp | 131 -- .../tstIncompressibleKOmegaEddyViscosity.cpp | 171 --- .../tstIncompressibleKOmegaSource.cpp | 193 --- ...essibleRealizableKEpsilonEddyViscosity.cpp | 143 -- ...IncompressibleRealizableKEpsilonSource.cpp | 160 -- ...eSpalartAllmarasDiffusivityCoefficient.cpp | 113 -- ...mpressibleSpalartAllmarasEddyViscosity.cpp | 111 -- ...tstIncompressibleSpalartAllmarasSource.cpp | 191 --- ...stIncompressibleVariableConvectiveFlux.cpp | 122 -- ...tstIncompressibleVariableDiffusionFlux.cpp | 132 -- .../tstIncompressibleWALEEddyViscosity.cpp | 147 -- src/utils/CMakeLists.txt | 57 - src/utils/VertexCFD_EvaluatorBase.hpp | 47 - src/utils/VertexCFD_EvaluatorBase_impl.hpp | 77 - src/utils/VertexCFD_Utils_Constants.hpp | 22 - ...FD_Utils_ExplicitTemplateInstantiation.hpp | 47 - src/utils/VertexCFD_Utils_KokkosFadFixup.hpp | 8 - src/utils/VertexCFD_Utils_MatrixMath.hpp | 105 -- src/utils/VertexCFD_Utils_NonlinearSolver.hpp | 292 ---- src/utils/VertexCFD_Utils_ParameterPack.hpp | 184 --- src/utils/VertexCFD_Utils_ScalarFieldView.hpp | 62 - src/utils/VertexCFD_Utils_ScalarToVector.hpp | 55 - .../VertexCFD_Utils_ScalarToVector_impl.hpp | 139 -- src/utils/VertexCFD_Utils_SmoothMath.hpp | 250 ---- src/utils/VertexCFD_Utils_TypeTraits.hpp | 63 - src/utils/VertexCFD_Utils_VectorField.hpp | 90 -- ...texCFD_Utils_VectorizeOutputFieldNames.hpp | 56 - src/utils/VertexCFD_Utils_VelocityDim.cpp | 13 - src/utils/VertexCFD_Utils_VelocityDim.hpp | 28 - src/utils/VertexCFD_Utils_VelocityLayout.cpp | 35 - src/utils/VertexCFD_Utils_VelocityLayout.hpp | 22 - src/utils/VertexCFD_Utils_Version.cpp | 18 - src/utils/VertexCFD_Utils_Version.hpp | 19 - src/utils/VertexCFD_Utils_config.hpp.cmakein | 8 - src/utils/unit_test/CMakeLists.txt | 19 - .../unit_test/doc/matrixMathReference.nb | 1173 --------------- .../unit_test/doc/nonlinearSolverReference.py | 40 - .../unit_test/doc/smoothMath_reference.py | 196 --- src/utils/unit_test/tstConstants.cpp | 58 - src/utils/unit_test/tstMatrixMath.cpp | 239 --- src/utils/unit_test/tstNonlinearSolver.cpp | 450 ------ src/utils/unit_test/tstParameterPack.cpp | 66 - src/utils/unit_test/tstScalarToVector.cpp | 225 --- src/utils/unit_test/tstSmoothMath.cpp | 1162 --------------- src/utils/unit_test/tstTypeTraits.cpp | 241 --- .../tstVectorizeOutputFieldNames.cpp | 81 -- src/utils/unit_test/tstVersion.cpp | 23 - 735 files changed, 78582 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 examples/CMakeLists.txt delete mode 100644 examples/README.md delete mode 100644 examples/inputs/full_induction_mhd/current_sheet_2d.xml delete mode 100644 examples/inputs/full_induction_mhd/divergence_advection_2d.xml delete mode 100644 examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml delete mode 100644 examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb_cuda.xml delete mode 100644 examples/inputs/full_induction_mhd/ldc_2d_bx_010.xml delete mode 100644 examples/inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_channel_periodic.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_heated_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_planar_poiseuille.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_model.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_taylor_green_vortex.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_tee_junction.xml delete mode 100644 examples/inputs/incompressible/incompressible_3d_channel_periodic.xml delete mode 100644 examples/inputs/incompressible/incompressible_3d_wale_cavity.xml delete mode 100644 examples/inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml delete mode 100644 examples/inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml delete mode 100644 examples/inputs/incompressible/incompressible_oscillating_laminar_flow_2d.xml delete mode 100644 examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml delete mode 100644 examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml delete mode 100644 examples/inputs/simple_box_2d.xml delete mode 100644 examples/inputs/simple_box_3d.xml delete mode 100644 examples/mesh/incompressible/2d_backward_facing_step.exo delete mode 100644 examples/mesh/incompressible/2d_concentric_convection.exo delete mode 100644 examples/mesh/incompressible/2d_concentric_cylinders_rad10.exo delete mode 100644 examples/mesh/incompressible/2d_concentric_cylinders_rad20.exo delete mode 100644 examples/mesh/incompressible/2d_concentric_cylinders_rad40.exo delete mode 100644 examples/mesh/incompressible/2d_concentric_cylinders_rad80.exo delete mode 100644 examples/mesh/incompressible/2d_cyclinder_vertex_quad.exo delete mode 100644 examples/mesh/incompressible/2d_tee_junction.exo delete mode 100644 examples/mesh/incompressible/bluntplate_square.exo delete mode 100644 examples/mesh/incompressible/half_turbulent_channel_mesh_one.exo delete mode 100644 examples/mesh/incompressible/pipe_hex.exo delete mode 100644 examples/mesh/incompressible/turbulent_channel_mesh_one.exo delete mode 100644 examples/mesh/test_mesh_manager.exo delete mode 100644 examples/post_processing/lambda-2-contour.py delete mode 100644 src/CMakeLists.txt delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.cpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_Factory.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.cpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.cpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.cpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.cpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp delete mode 100644 src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter_impl.hpp delete mode 100644 src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.cpp delete mode 100644 src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp delete mode 100644 src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector_impl.hpp delete mode 100644 src/boundary_conditions/unit_test/CMakeLists.txt delete mode 100644 src/boundary_conditions/unit_test/tstMethodManufacturedSolutionBC.cpp delete mode 100644 src/boundary_conditions/unit_test/tstViscousGradient.cpp delete mode 100644 src/boundary_conditions/unit_test/tstViscousPenaltyParameter.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory.hpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Hessian2d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Hessian3d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Jacobian2d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Jacobian3d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Residual2d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Residual3d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Tangent2d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_Tangent3d.cpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp delete mode 100644 src/closure_models/VertexCFD_ClosureModelFactory_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ConstantScalarField.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_ConstantScalarField.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ConstantScalarField_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ElementLength.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_ElementLength.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ElementLength_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalFields.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalFields.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalFields_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalMagneticField.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalMagneticField.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_ExternalMagneticField_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MeasureElementLength.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MeasureElementLength.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MeasureElementLength_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Hessian.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Jacobian.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Residual.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Tangent.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MethodManufacturedSolution_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensor.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensor.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensorElementLength.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensorElementLength_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_MetricTensor_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_SingularValueElementLength.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_SingularValueElementLength.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_SingularValueElementLength_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_VectorFieldDivergence.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_VectorFieldDivergence_impl.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_WallDistance.cpp delete mode 100644 src/closure_models/VertexCFD_Closure_WallDistance.hpp delete mode 100644 src/closure_models/VertexCFD_Closure_WallDistance_impl.hpp delete mode 100644 src/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp delete mode 100644 src/closure_models/unit_test/doc/MethodManufacuredSolution2D_reference.py delete mode 100644 src/closure_models/unit_test/doc/MethodManufacuredSolution3D_reference.py delete mode 100644 src/closure_models/unit_test/doc/MethodManufacuredSolutionSource2D_reference.nb delete mode 100644 src/closure_models/unit_test/doc/MethodManufacuredSolutionSource3D_reference.nb delete mode 100644 src/closure_models/unit_test/doc/external_magnetic_field.py delete mode 100644 src/closure_models/unit_test/doc/singularValue_reference.py delete mode 100644 src/closure_models/unit_test/tstClampField.cpp delete mode 100644 src/closure_models/unit_test/tstClosureModelFactoryTestHarness.cpp delete mode 100644 src/closure_models/unit_test/tstConstantScalarField.cpp delete mode 100644 src/closure_models/unit_test/tstElementLength.cpp delete mode 100644 src/closure_models/unit_test/tstExternalFields.cpp delete mode 100644 src/closure_models/unit_test/tstExternalMagneticField.cpp delete mode 100644 src/closure_models/unit_test/tstMeasureElementLength.cpp delete mode 100644 src/closure_models/unit_test/tstMethodManufacturedSolution.cpp delete mode 100644 src/closure_models/unit_test/tstMethodManufacturedSolutionSource.cpp delete mode 100644 src/closure_models/unit_test/tstMetricTensor.cpp delete mode 100644 src/closure_models/unit_test/tstMetricTensorElementLength.cpp delete mode 100644 src/closure_models/unit_test/tstSingularValueElementLength.cpp delete mode 100644 src/closure_models/unit_test/tstVectorFieldDivergence.cpp delete mode 100644 src/closure_models/unit_test/tstWallDistance.cpp delete mode 100644 src/drivers/VertexCFD_ExternalFieldsManager.hpp delete mode 100644 src/drivers/VertexCFD_ExternalFieldsManager_impl.hpp delete mode 100644 src/drivers/VertexCFD_InitialConditionManager.cpp delete mode 100644 src/drivers/VertexCFD_InitialConditionManager.hpp delete mode 100644 src/drivers/VertexCFD_MeshManager.cpp delete mode 100644 src/drivers/VertexCFD_MeshManager.hpp delete mode 100644 src/drivers/VertexCFD_PhysicsManager.cpp delete mode 100644 src/drivers/VertexCFD_PhysicsManager.hpp delete mode 100644 src/drivers/unit_test/CMakeLists.txt delete mode 100644 src/drivers/unit_test/VertexCFD_DriverUnitTestConfig.hpp.cmakein delete mode 100644 src/drivers/unit_test/data/simple_box_2d_restart.xml delete mode 100644 src/drivers/unit_test/data/simple_box_fim_3d.xml delete mode 100644 src/drivers/unit_test/tstInitialConditionManager.cpp delete mode 100644 src/drivers/unit_test/tstMeshManager.cpp delete mode 100644 src/drivers/unit_test/tstPhysicsManager.cpp delete mode 100644 src/drivers/vertexcfd.cpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_Factory.hpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_Heat.cpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_Heat.hpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_Heat_impl.hpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.cpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.hpp delete mode 100644 src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes_impl.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.cpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting_impl.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.cpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed_impl.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.cpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/unit_test/doc/fullInductionConducting_reference.py delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionConducting.cpp delete mode 100644 src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionFixed.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian2d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian3d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian2d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian3d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual2d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual3d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent2d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent3d.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_impl.hpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/divergenceCleaningSource_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/error_norms_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/fullInductionLocalTimeStepSize_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/godunovPowellSource_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionConvectiveFlux_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionResitiveFlux_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/doc/mhd_vortex_problem_exact_reference.py delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstDivergenceCleaningSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionErrorNorms.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionLocalTimeStepSize.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionTimeDerivative.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstGodunovPowellSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConstantSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConvectiveFlux.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstInductionResistiveFlux.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstMHDVortexProblemExact.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticCorrectionDampingSource.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticPressure.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstResistivityFactory.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticField.cpp delete mode 100644 src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticFieldGradient.cpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.cpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory_impl.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.cpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.cpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/unit_test/CMakeLists.txt delete mode 100644 src/full_induction_mhd_solver/initial_conditions/unit_test/tstDivergenceAdvectionTest.cpp delete mode 100644 src/full_induction_mhd_solver/initial_conditions/unit_test/tstMHDVortexProblem.cpp delete mode 100644 src/full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp delete mode 100644 src/full_induction_mhd_solver/mhd_properties/unit_test/CMakeLists.txt delete mode 100644 src/full_induction_mhd_solver/mhd_properties/unit_test/tstFullInductionMHDProperties.cpp delete mode 100644 src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.cpp delete mode 100644 src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp delete mode 100644 src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux_impl.hpp delete mode 100644 src/incompressible_lsvof_solver/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/incompressible_lsvof_solver/closure_models/unit_test/tstIncompressibleLSVOFConvectiveFlux.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction_impl.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/CMakeLists.txt delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_cavity_flow_reference.py delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_laminar_flow_reference.py delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_symmetry_reference.py delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_wall_function_reference.py delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/doc/time_dependent_incompressible_rotating_wall_reference.py delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleBoundaryLaminarFlow.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleCavityLid.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleFreeSlip.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleNoSlip.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressiblePressureOutflow.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleSymmetry.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleWallFunction.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleDirichlet.cpp delete mode 100644 src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleRotatingWall.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian2d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian3d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian2d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian3d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual2d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual3d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent2d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent3d.cpp delete mode 100644 src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_impl.hpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/convective_flux_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/error_norms_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/liftdrag_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/local_time_step_size_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/planar_poiseuille_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/rotating_annulus_exact_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/shear_variables_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/taylor_green_vortex_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/viscous_flux_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/doc/viscous_heat_reference.py delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleBuoyancySource.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleConstantSource.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleConvectiveFlux.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleErrorNorms.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleLiftDrag.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleLocalTimeStepSize.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressiblePlanarPoiseuilleExact.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleRotatingAnnulusExact.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleShearVariables.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleTaylorGreenVortexExactSolution.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleTimeDerivative.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleVariableTimeDerivative.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousFlux.cpp delete mode 100644 src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousHeat.cpp delete mode 100644 src/incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp delete mode 100644 src/incompressible_solver/fluid_properties/unit_test/CMakeLists.txt delete mode 100644 src/incompressible_solver/fluid_properties/unit_test/tstIncompressibleConstantFluidProperties.cpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.cpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow_impl.hpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.cpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex_impl.hpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.cpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp delete mode 100644 src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox_impl.hpp delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/CMakeLists.txt delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_laminar_flow_reference.py delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_taylor_green_vortex_reference.py delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_vortex_box_reference.py delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleLaminarFlow.cpp delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleTaylorGreenVortex.cpp delete mode 100644 src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleVortexInBox.cpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.cpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed_impl.hpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.cpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall_impl.hpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.cpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/unit_test/doc/insulating_wall_reference.py delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/unit_test/tstElectricPotentialInsulatingWall.cpp delete mode 100644 src/induction_less_mhd_solver/boundary_conditions/unit_test/tstTimeTransientElectricPotentialFixed.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian2d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian3d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian2d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian3d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual2d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual3d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent2d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent3d.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_impl.hpp delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_cross_product_flux.py delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_current_density_reference.py delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_lorentz_force_reference.py delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_hartmann_problem.py delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/tstElectricCurrentDensity.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialCrossProductFlux.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialDiffusionFlux.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/tstHartmannProblem.cpp delete mode 100644 src/induction_less_mhd_solver/closure_models/unit_test/tstLorentzForce.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialConditionFactory.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialConditionFactory.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialConditionFactory_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Circle.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Circle.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Circle_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Constant.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Constant.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Constant_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Gaussian.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Gaussian_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Step.cpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Step.hpp delete mode 100644 src/initial_conditions/VertexCFD_InitialCondition_Step_impl.hpp delete mode 100644 src/initial_conditions/unit_test/CMakeLists.txt delete mode 100644 src/initial_conditions/unit_test/doc/gaussian_reference.py delete mode 100644 src/initial_conditions/unit_test/tstInitialConditionCircle.cpp delete mode 100644 src/initial_conditions/unit_test/tstInitialConditionConstant.cpp delete mode 100644 src/initial_conditions/unit_test/tstInitialConditionGaussian.cpp delete mode 100644 src/initial_conditions/unit_test/tstInitialConditionStep.cpp delete mode 100644 src/initial_conditions/unit_test/tstMethodManufacturedSolutionIC.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_CusolverNonpublic.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_LocalDirectSolver.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.hpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.cpp delete mode 100644 src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.hpp delete mode 100644 src/linear_solvers/unit_test/CMakeLists.txt delete mode 100644 src/linear_solvers/unit_test/VertexCFD_SolverTester.hpp delete mode 100644 src/linear_solvers/unit_test/tstCusolverGLU.cpp delete mode 100644 src/linear_solvers/unit_test/tstLocalSolverFactory.cpp delete mode 100644 src/linear_solvers/unit_test/tstPreconditioner.cpp delete mode 100644 src/linear_solvers/unit_test/tstPreconditionerFactory.cpp delete mode 100644 src/mesh/VertexCFD_Mesh_ExodusWriter.cpp delete mode 100644 src/mesh/VertexCFD_Mesh_ExodusWriter.hpp delete mode 100644 src/mesh/VertexCFD_Mesh_GeometryData.hpp delete mode 100644 src/mesh/VertexCFD_Mesh_GeometryPrimitives.hpp delete mode 100644 src/mesh/VertexCFD_Mesh_Restart.cpp delete mode 100644 src/mesh/VertexCFD_Mesh_Restart.hpp delete mode 100644 src/mesh/VertexCFD_Mesh_StkReaderFactory.cpp delete mode 100644 src/mesh/VertexCFD_Mesh_StkReaderFactory.hpp delete mode 100644 src/mesh/unit_test/CMakeLists.txt delete mode 100644 src/mesh/unit_test/test_data/.gitattributes delete mode 100644 src/mesh/unit_test/test_data/read_only_test.restart.data delete mode 100644 src/mesh/unit_test/test_data/read_only_test.restart.dofmap delete mode 100644 src/mesh/unit_test/test_data/read_only_test_periodic.restart.data delete mode 100644 src/mesh/unit_test/test_data/read_only_test_periodic.restart.dofmap delete mode 100644 src/mesh/unit_test/tstGeometryPrimitives.cpp delete mode 100644 src/mesh/unit_test/tstRestart.cpp delete mode 100644 src/observers/VertexCFD_Compute_ErrorNorms.hpp delete mode 100644 src/observers/VertexCFD_Compute_ErrorNorms_impl.hpp delete mode 100644 src/observers/VertexCFD_Compute_Volume.hpp delete mode 100644 src/observers/VertexCFD_Compute_Volume_impl.hpp delete mode 100644 src/observers/VertexCFD_NOXObserver_IterationOutput.cpp delete mode 100644 src/observers/VertexCFD_NOXObserver_IterationOutput.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_IterationOutput.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_IterationOutput_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_ResponseOutput.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_ResponseOutput_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteMatrix.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteMatrix_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteRestart.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteRestart_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteToExodus.hpp delete mode 100644 src/observers/VertexCFD_TempusObserver_WriteToExodus_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp delete mode 100644 src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp delete mode 100644 src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep_impl.hpp delete mode 100644 src/observers/VertexCFD_TempusTimeStepControl_Strategy.hpp delete mode 100644 src/observers/unit_test/CDR_Model.hpp delete mode 100644 src/observers/unit_test/CDR_Model_impl.hpp delete mode 100644 src/observers/unit_test/CMakeLists.txt delete mode 100644 src/observers/unit_test/tstWriteMatrix.cpp delete mode 100644 src/parameters/VertexCFD_GeneralScalarParameter.cpp delete mode 100644 src/parameters/VertexCFD_GeneralScalarParameter.hpp delete mode 100644 src/parameters/VertexCFD_GeneralScalarParameterInput.cpp delete mode 100644 src/parameters/VertexCFD_GeneralScalarParameterInput.hpp delete mode 100644 src/parameters/VertexCFD_GeneralScalarParameter_impl.hpp delete mode 100644 src/parameters/VertexCFD_ParameterDatabase.cpp delete mode 100644 src/parameters/VertexCFD_ParameterDatabase.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameter.cpp delete mode 100644 src/parameters/VertexCFD_ScalarParameter.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterEvaluator.cpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterEvaluator.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterEvaluator_impl.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterInput.cpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterInput.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterManager.cpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterManager.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterManager_impl.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterObserver.cpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterObserver.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameterObserver_impl.hpp delete mode 100644 src/parameters/VertexCFD_ScalarParameter_impl.hpp delete mode 100644 src/parameters/unit_test/CMakeLists.txt delete mode 100644 src/parameters/unit_test/VertexCFD_ParameterUnitTestConfig.hpp.cmakein delete mode 100644 src/parameters/unit_test/data/input_parser_test.xml delete mode 100644 src/parameters/unit_test/data/scalar_parameter_evaluator_test.xml delete mode 100644 src/parameters/unit_test/tstGeneralScalarParameter.cpp delete mode 100644 src/parameters/unit_test/tstGeneralScalarParameterInput.cpp delete mode 100644 src/parameters/unit_test/tstParameterDatabase.cpp delete mode 100644 src/parameters/unit_test/tstScalarParameter.cpp delete mode 100644 src/parameters/unit_test/tstScalarParameterEvaluator.cpp delete mode 100644 src/parameters/unit_test/tstScalarParameterInput.cpp delete mode 100644 src/parameters/unit_test/tstScalarParameterObserver.cpp delete mode 100644 src/responses/VertexCFD_ResponseManager.cpp delete mode 100644 src/responses/VertexCFD_ResponseManager.hpp delete mode 100644 src/responses/VertexCFD_Response_Utils.cpp delete mode 100644 src/responses/VertexCFD_Response_Utils.hpp delete mode 100644 src/responses/unit_test/CMakeLists.txt delete mode 100644 src/responses/unit_test/VertexCFD_ResponseUnitTestConfig.hpp.cmakein delete mode 100644 src/responses/unit_test/data/response_manager_test.xml delete mode 100644 src/responses/unit_test/tstResponseManager.cpp delete mode 100644 src/responses/unit_test/tstResponseUtils.cpp delete mode 100644 src/test_harness/CMakeLists.txt delete mode 100644 src/test_harness/TestHarness.cmake delete mode 100644 src/test_harness/VertexCFD_EvaluatorTestHarness.hpp delete mode 100644 src/test_harness/test_main.cpp delete mode 100644 src/test_harness/unit_test/CMakeLists.txt delete mode 100644 src/test_harness/unit_test/tstEvaluatorTestHarness.cpp delete mode 100644 src/test_harness/unit_test/tstKokkosMPI.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry_impl.hpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.cpp delete mode 100644 src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/CMakeLists.txt delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/doc/KEpsilon_Wall_Function_reference.py delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/doc/turbulence_symmetry_reference.py delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceBoundaryEddyViscosity.cpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceExtrapolate.cpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceFixed.cpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceInletOutlet.cpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceKEpsilonWallFunction.cpp delete mode 100644 src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceSymmetry.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity_impl.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian2d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian3d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian2d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian3d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual2d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual3d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent2d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent3d.cpp delete mode 100644 src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_impl.hpp delete mode 100644 src/turbulence_models/closure_models/unit_test/CMakeLists.txt delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/KEpsilonSource_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/KOmegaEddyViscosity_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/KOmegaSource_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonEddyViscosity_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonSource_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/SADiffusivityCoefficient_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/SAEddyViscosity_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/SASource_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/doc/WALEEddyViscosity_reference.py delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonSource.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaSource.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonSource.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasDiffusivityCoefficient.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasEddyViscosity.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasSource.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableConvectiveFlux.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableDiffusionFlux.cpp delete mode 100644 src/turbulence_models/closure_models/unit_test/tstIncompressibleWALEEddyViscosity.cpp delete mode 100644 src/utils/CMakeLists.txt delete mode 100644 src/utils/VertexCFD_EvaluatorBase.hpp delete mode 100644 src/utils/VertexCFD_EvaluatorBase_impl.hpp delete mode 100644 src/utils/VertexCFD_Utils_Constants.hpp delete mode 100644 src/utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp delete mode 100644 src/utils/VertexCFD_Utils_KokkosFadFixup.hpp delete mode 100644 src/utils/VertexCFD_Utils_MatrixMath.hpp delete mode 100644 src/utils/VertexCFD_Utils_NonlinearSolver.hpp delete mode 100644 src/utils/VertexCFD_Utils_ParameterPack.hpp delete mode 100644 src/utils/VertexCFD_Utils_ScalarFieldView.hpp delete mode 100644 src/utils/VertexCFD_Utils_ScalarToVector.hpp delete mode 100644 src/utils/VertexCFD_Utils_ScalarToVector_impl.hpp delete mode 100644 src/utils/VertexCFD_Utils_SmoothMath.hpp delete mode 100644 src/utils/VertexCFD_Utils_TypeTraits.hpp delete mode 100644 src/utils/VertexCFD_Utils_VectorField.hpp delete mode 100644 src/utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp delete mode 100644 src/utils/VertexCFD_Utils_VelocityDim.cpp delete mode 100644 src/utils/VertexCFD_Utils_VelocityDim.hpp delete mode 100644 src/utils/VertexCFD_Utils_VelocityLayout.cpp delete mode 100644 src/utils/VertexCFD_Utils_VelocityLayout.hpp delete mode 100644 src/utils/VertexCFD_Utils_Version.cpp delete mode 100644 src/utils/VertexCFD_Utils_Version.hpp delete mode 100644 src/utils/VertexCFD_Utils_config.hpp.cmakein delete mode 100644 src/utils/unit_test/CMakeLists.txt delete mode 100644 src/utils/unit_test/doc/matrixMathReference.nb delete mode 100644 src/utils/unit_test/doc/nonlinearSolverReference.py delete mode 100644 src/utils/unit_test/doc/smoothMath_reference.py delete mode 100644 src/utils/unit_test/tstConstants.cpp delete mode 100644 src/utils/unit_test/tstMatrixMath.cpp delete mode 100644 src/utils/unit_test/tstNonlinearSolver.cpp delete mode 100644 src/utils/unit_test/tstParameterPack.cpp delete mode 100644 src/utils/unit_test/tstScalarToVector.cpp delete mode 100644 src/utils/unit_test/tstSmoothMath.cpp delete mode 100644 src/utils/unit_test/tstTypeTraits.cpp delete mode 100644 src/utils/unit_test/tstVectorizeOutputFieldNames.cpp delete mode 100644 src/utils/unit_test/tstVersion.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a6ea744..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,234 +0,0 @@ -#------------------------------------------------------------------------------# -# Project settings -#------------------------------------------------------------------------------# - -cmake_minimum_required(VERSION 3.9) -include(CheckLanguage) - -project(VertexCFD LANGUAGES CXX) -check_language(CUDA) -if (CMAKE_CUDA_COMPILER) - enable_language(CUDA) -endif () -set(PROJECT_VERSION "0.0-dev" ) - -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) -endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) #...without compiler extensions like gnu++11 - -option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF) - -include(GNUInstallDirs) - -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) - -option(NO_PARMETIS_SUPPORT "No parmetis support" ON) -add_compile_definitions(NO_PARMETIS_SUPPORT) - -# Add the installation library dir to the executable RPATH -list(APPEND CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") - -# Add paths to linked libraries outside the build tree to the RPATH -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -#------------------------------------------------------------------------------# -# Dependencies -#------------------------------------------------------------------------------# - -find_package(CLANG_FORMAT) - -find_package(YAPF) - -find_package(MPI REQUIRED) - -find_package(Trilinos REQUIRED) -if (Trilinos_VERSION VERSION_LESS 13 OR Trilinos_VERSION VERSION_GREATER_EQUAL 15) - message(FATAL_ERROR "Incompatible Trilinos version ${Trilinos_VERSION}. Only Trilinos versions 13 and 14 are supported.") -endif () - -find_package(Python COMPONENTS Interpreter Development.Module) - -find_package(pybind11) - -if (VertexCFD_ENABLE_SUPERLU) - find_package(SuperLU_dist REQUIRED) - add_compile_definitions(HAVE_SUPERLUDIST) -endif() - -#------------------------------------------------------------------------------# -# Performance Portability -# ------------------------------------------------------------------------------# -# Set the kokkos node type used by panzer/phalanx. We do this because phalanx -# explicitly sets a node type to be used by all of its data structures which -# is subsequently used by panzer. Below we copy the logic from Phalanx for -# choosing the default node type so our choice matches that of Panzer. The -# phalanx CMake variable ${Phalanx_KOKKOS_DEVICE_TYPE} is not exported and -# therefore is not accessible here which would have been perferable. -if (Kokkos_ENABLE_CUDA) - set(VERTEXCFD_KOKKOS_DEVICE_TYPE "CUDA") -elseif (Kokkos_ENABLE_OPENMP) - set(VERTEXCFD_KOKKOS_DEVICE_TYPE "OPENMP") -elseif (Kokkos_ENABLE_SERIAL) - set(VERTEXCFD_KOKKOS_DEVICE_TYPE "SERIAL") -else() - message(FATAL_ERROR "No Kokkos execution space is enabled.") -endif() - -# Check that the kokkos device type we want is available. -kokkos_check( DEVICES ${VERTEXCFD_KOKKOS_DEVICE_TYPE} ) - -# ensure that we can use lambdas if we are using cuda. -if(${VERTEXCFD_KOKKOS_DEVICE_TYPE} STREQUAL "CUDA") - kokkos_check(OPTIONS CUDA_LAMBDA) -endif() - -# output Phalanx/Panzer device type -message( STATUS "Phalanx/Panzer Kokkos device type: ${VERTEXCFD_KOKKOS_DEVICE_TYPE}" ) - -if (CMAKE_CUDA_COMPILER) - find_package(CUDAToolkit) -endif () - -#------------------------------------------------------------------------------# -# Tests and Documentation -#------------------------------------------------------------------------------# - -option(VertexCFD_ENABLE_TESTING "Build tests" OFF) -if(VertexCFD_ENABLE_TESTING) - include(CTest) - find_package(GTest REQUIRED) - enable_testing() -endif() - -if (VertexCFD_BUILD_DOC) - find_package(Doxygen) - if (DOXYGEN_FOUND) - # ignore dep packages like gtest/gmock - set(DOXYGEN_EXCLUDE_PATTERNS "*/_deps/*") - doxygen_add_docs( vertexcfddocs - ${PROJECT_SOURCE_DIR} - COMMENT "Generate doxygen pages" - ) - else (DOXYGEN_FOUND) - message(STATUS "Doxygen need to be installed to generate the doxygen documentation") - endif (DOXYGEN_FOUND) -else (VertexCFD_BUILD_DOC) - message(STATUS "Doxygen documentation disabled!") -endif (VertexCFD_BUILD_DOC) - - -##---------------------------------------------------------------------------## -## Code coverage testing -##---------------------------------------------------------------------------## -option(VertexCFD_ENABLE_COVERAGE_BUILD "Do a coverage build" OFF) -if(VertexCFD_ENABLE_COVERAGE_BUILD) - message(STATUS "Enabling coverage build") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -O0") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") -endif() - -##---------------------------------------------------------------------------## -## Print the revision number to stdout -##---------------------------------------------------------------------------## -find_package(Git) -if(GIT_FOUND AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.git) - execute_process( - COMMAND ${GIT_EXECUTABLE} log --pretty=format:%H -n 1 - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE VertexCFD_GIT_COMMIT_HASH - ) -else() - set(VertexCFD_GIT_COMMIT_HASH "Not a git repository") -endif() -message(STATUS "VertexCFD Revision = '${VertexCFD_GIT_COMMIT_HASH}'") - -##---------------------------------------------------------------------------## -## Library -##---------------------------------------------------------------------------## - -add_subdirectory(src) - -add_subdirectory(examples) - -file(COPY regression_test DESTINATION .) - -##---------------------------------------------------------------------------## -## Package Configuration -##---------------------------------------------------------------------------## -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/VertexCFDConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/VertexCFDConfig.cmake @ONLY) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/VertexCFDConfig.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VertexCFD) - -##---------------------------------------------------------------------------## -## Clang Format -##---------------------------------------------------------------------------## -if(CLANG_FORMAT_FOUND) - file(GLOB_RECURSE FORMAT_SOURCES src/*.cpp src/*.hpp) - add_custom_target(format-cpp - COMMAND ${CLANG_FORMAT_EXECUTABLE} -i -style=file ${FORMAT_SOURCES} - DEPENDS ${FORMAT_SOURCES}) - add_custom_target(format-cpp-dry-run - COMMAND ${CLANG_FORMAT_EXECUTABLE} -i -style=file --dry-run -Werror ${FORMAT_SOURCES} - DEPENDS ${FORMAT_SOURCES}) -endif() - -##---------------------------------------------------------------------------## -## Python Format -##---------------------------------------------------------------------------## -if(YAPF_FOUND) - file(GLOB_RECURSE FORMAT_SOURCES *.py) - add_custom_target(format-python - COMMAND ${YAPF_EXECUTABLE} -i -vv ${FORMAT_SOURCES} - DEPENDS ${FORMAT_SOURCES}) - add_custom_target(format-python-dry-run - COMMAND ${YAPF_EXECUTABLE} -d ${FORMAT_SOURCES} - DEPENDS ${FORMAT_SOURCES}) -endif() - -##---------------------------------------------------------------------------## -## Python and Clang Format -##---------------------------------------------------------------------------## -if(YAPF_FOUND OR CLANG_FORMAT_FOUND) - add_custom_target(format) - if(CLANG_FORMAT_FOUND) - add_dependencies(format format-cpp) - endif() - if(YAPF_FOUND) - add_dependencies(format format-python) - endif() -endif() - -##---------------------------------------------------------------------------## -## Code coverage report generation -##---------------------------------------------------------------------------## -if(VertexCFD_ENABLE_COVERAGE_BUILD) - message(CHECK_START "Checking for gcovr") - list(APPEND CMAKE_MESSAGE_INDENT " ") - find_package(GCOVR) - list(POP_BACK CMAKE_MESSAGE_INDENT) - if(NOT GCOVR_FOUND) - message(CHECK_FAIL "not found. Coverage report targets disabled.") - else() - message(CHECK_PASS "found. Adding coverage report targets.") - list(APPEND CMAKE_MESSAGE_INDENT " ") - set(gcovr_filter "'${PROJECT_SOURCE_DIR}/src/.*'") - message(STATUS "Adding gcovr_xml target") - add_custom_target(gcovr_xml - COMMAND ${GCOVR_EXECUTABLE} - --filter=${gcovr_filter} --xml-pretty --print-summary -o coverage.xml - -r "${PROJECT_SOURCE_DIR}" .) - message(STATUS "Adding gcovr_html target") - add_custom_target(gcovr_html - COMMAND ${GCOVR_EXECUTABLE} - --filter=${gcovr_filter} --html-details coverage/ - -r "${PROJECT_SOURCE_DIR}" .) - list(POP_BACK CMAKE_MESSAGE_INDENT) - endif() -endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt deleted file mode 100644 index 48e9aac..0000000 --- a/examples/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -set(REGRESSION_XML_FILES - inputs/incompressible/incompressible_2d_backward_facing_step.xml - inputs/incompressible/incompressible_2d_channel.xml - inputs/incompressible/incompressible_2d_channel_periodic.xml - inputs/incompressible/incompressible_3d_channel_periodic.xml - inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml - inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml - inputs/incompressible/incompressible_2d_heated_channel.xml - inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml - inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml - inputs/incompressible/incompressible_2d_taylor_green_vortex.xml - inputs/incompressible/incompressible_2d_tee_junction.xml - inputs/incompressible/incompressible_2d_planar_poiseuille.xml - inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml - inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml - inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml - inputs/incompressible/incompressible_3d_wale_cavity.xml - inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml - inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml - inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml - inputs/full_induction_mhd/divergence_advection_2d.xml - inputs/full_induction_mhd/current_sheet_2d.xml - inputs/full_induction_mhd/ldc_2d_bx_010.xml - inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml - ) -file(COPY ${REGRESSION_XML_FILES} DESTINATION .) - -set(REGRESSION_EXO_FILES - mesh/incompressible/2d_backward_facing_step.exo - mesh/incompressible/pipe_hex.exo - mesh/incompressible/2d_cyclinder_vertex_quad.exo - mesh/incompressible/bluntplate_square.exo - mesh/incompressible/2d_concentric_cylinders_rad10.exo - mesh/incompressible/2d_concentric_cylinders_rad20.exo - mesh/incompressible/2d_concentric_cylinders_rad40.exo - mesh/incompressible/2d_concentric_cylinders_rad80.exo - mesh/incompressible/2d_concentric_convection.exo - mesh/incompressible/turbulent_channel_mesh_one.exo - mesh/incompressible/2d_tee_junction.exo - mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo - mesh/full_induction_mhd/ldc_bl_81x81.exo - ) -file(COPY ${REGRESSION_EXO_FILES} DESTINATION .) - -install(FILES - inputs/incompressible/incompressible_2d_backward_facing_step.xml - inputs/incompressible/incompressible_2d_channel.xml - inputs/incompressible/incompressible_2d_channel_periodic.xml - inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml - inputs/incompressible/incompressible_2d_channel.xml - inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml - inputs/incompressible/incompressible_3d_channel_periodic.xml - inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml - inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml - inputs/incompressible/incompressible_2d_planar_poiseuille.xml - inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_tee_junction.xml - inputs/incompressible/incompressible_3d_wale_cavity.xml - DESTINATION examples/incompressible) - -install(FILES - mesh/incompressible/2d_backward_facing_step.exo - mesh/incompressible/2d_cyclinder_vertex_quad.exo - mesh/incompressible/bluntplate_square.exo - mesh/incompressible/pipe_hex.exo - mesh/incompressible/2d_concentric_cylinders_rad40.exo - mesh/incompressible/2d_concentric_convection.exo - mesh/incompressible/2d_tee_junction.exo - DESTINATION examples/incompressible) diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 649321b..0000000 --- a/examples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This directory contains input files in XML format that are used for regression testing but also could be used as a starting -point for the development of new input files to run with VertexCFD. The input files are stored in the directory `inputs` while -the meshes are stored in `meshes`. diff --git a/examples/inputs/full_induction_mhd/current_sheet_2d.xml b/examples/inputs/full_induction_mhd/current_sheet_2d.xml deleted file mode 100644 index 34fca4e..0000000 --- a/examples/inputs/full_induction_mhd/current_sheet_2d.xml +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/full_induction_mhd/divergence_advection_2d.xml b/examples/inputs/full_induction_mhd/divergence_advection_2d.xml deleted file mode 100644 index 365a803..0000000 --- a/examples/inputs/full_induction_mhd/divergence_advection_2d.xml +++ /dev/null @@ -1,299 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml b/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml deleted file mode 100644 index 23862b3..0000000 --- a/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml +++ /dev/null @@ -1,291 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb_cuda.xml b/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb_cuda.xml deleted file mode 100644 index f687cb5..0000000 --- a/examples/inputs/full_induction_mhd/full_induction_vortex_2d_pb_cuda.xml +++ /dev/null @@ -1,285 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/full_induction_mhd/ldc_2d_bx_010.xml b/examples/inputs/full_induction_mhd/ldc_2d_bx_010.xml deleted file mode 100644 index 2e1bd10..0000000 --- a/examples/inputs/full_induction_mhd/ldc_2d_bx_010.xml +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml b/examples/inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml deleted file mode 100644 index ec7cc6c..0000000 --- a/examples/inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml +++ /dev/null @@ -1,441 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml b/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml deleted file mode 100644 index e4ba60b..0000000 --- a/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_channel.xml b/examples/inputs/incompressible/incompressible_2d_channel.xml deleted file mode 100644 index 64dec08..0000000 --- a/examples/inputs/incompressible/incompressible_2d_channel.xml +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_channel_periodic.xml b/examples/inputs/incompressible/incompressible_2d_channel_periodic.xml deleted file mode 100644 index 3687fe8..0000000 --- a/examples/inputs/incompressible/incompressible_2d_channel_periodic.xml +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml b/examples/inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml deleted file mode 100644 index f0031ae..0000000 --- a/examples/inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_heated_channel.xml b/examples/inputs/incompressible/incompressible_2d_heated_channel.xml deleted file mode 100644 index 6c31718..0000000 --- a/examples/inputs/incompressible/incompressible_2d_heated_channel.xml +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_planar_poiseuille.xml b/examples/inputs/incompressible/incompressible_2d_planar_poiseuille.xml deleted file mode 100644 index 4898d51..0000000 --- a/examples/inputs/incompressible/incompressible_2d_planar_poiseuille.xml +++ /dev/null @@ -1,311 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml b/examples/inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml deleted file mode 100644 index 0d94a82..0000000 --- a/examples/inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml b/examples/inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml deleted file mode 100644 index e9d9788..0000000 --- a/examples/inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml b/examples/inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml deleted file mode 100644 index 7b3389b..0000000 --- a/examples/inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml b/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml deleted file mode 100644 index f29e9b7..0000000 --- a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml b/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml deleted file mode 100644 index 1e85c1f..0000000 --- a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml +++ /dev/null @@ -1,366 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_model.xml b/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_model.xml deleted file mode 100644 index bae9f57..0000000 --- a/examples/inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_model.xml +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml b/examples/inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml deleted file mode 100644 index deb52cf..0000000 --- a/examples/inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_taylor_green_vortex.xml b/examples/inputs/incompressible/incompressible_2d_taylor_green_vortex.xml deleted file mode 100644 index 020e151..0000000 --- a/examples/inputs/incompressible/incompressible_2d_taylor_green_vortex.xml +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_tee_junction.xml b/examples/inputs/incompressible/incompressible_2d_tee_junction.xml deleted file mode 100644 index 9f59342..0000000 --- a/examples/inputs/incompressible/incompressible_2d_tee_junction.xml +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_3d_channel_periodic.xml b/examples/inputs/incompressible/incompressible_3d_channel_periodic.xml deleted file mode 100644 index 981746a..0000000 --- a/examples/inputs/incompressible/incompressible_3d_channel_periodic.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_3d_wale_cavity.xml b/examples/inputs/incompressible/incompressible_3d_wale_cavity.xml deleted file mode 100644 index 1940b7d..0000000 --- a/examples/inputs/incompressible/incompressible_3d_wale_cavity.xml +++ /dev/null @@ -1,358 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml b/examples/inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml deleted file mode 100644 index 84f9873..0000000 --- a/examples/inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml b/examples/inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml deleted file mode 100644 index 2db1085..0000000 --- a/examples/inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml +++ /dev/null @@ -1,351 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_oscillating_laminar_flow_2d.xml b/examples/inputs/incompressible/incompressible_oscillating_laminar_flow_2d.xml deleted file mode 100644 index 845c27a..0000000 --- a/examples/inputs/incompressible/incompressible_oscillating_laminar_flow_2d.xml +++ /dev/null @@ -1,290 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml b/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml deleted file mode 100644 index 70e9f94..0000000 --- a/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml +++ /dev/null @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml b/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml deleted file mode 100644 index fa211a0..0000000 --- a/examples/inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/simple_box_2d.xml b/examples/inputs/simple_box_2d.xml deleted file mode 100644 index 98a16b4..0000000 --- a/examples/inputs/simple_box_2d.xml +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/simple_box_3d.xml b/examples/inputs/simple_box_3d.xml deleted file mode 100644 index 4e3dd41..0000000 --- a/examples/inputs/simple_box_3d.xml +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/mesh/incompressible/2d_backward_facing_step.exo b/examples/mesh/incompressible/2d_backward_facing_step.exo deleted file mode 100644 index b3181a3..0000000 --- a/examples/mesh/incompressible/2d_backward_facing_step.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5bbd1325ca2e4b70a7294153b86550d1b04e6995dbeb9cec40e0a44f48ac98c -size 807604 diff --git a/examples/mesh/incompressible/2d_concentric_convection.exo b/examples/mesh/incompressible/2d_concentric_convection.exo deleted file mode 100644 index 6b53d1b..0000000 --- a/examples/mesh/incompressible/2d_concentric_convection.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:907b99feb601f07b4564aa03b20e534171d2ac5ad338d45c44c28a41956b853f -size 114440 diff --git a/examples/mesh/incompressible/2d_concentric_cylinders_rad10.exo b/examples/mesh/incompressible/2d_concentric_cylinders_rad10.exo deleted file mode 100644 index 8dd5445..0000000 --- a/examples/mesh/incompressible/2d_concentric_cylinders_rad10.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5292f05779eecb20d7d89e8dd4cc915f7b22844529a2d944a1dc502949ea82fc -size 32520 diff --git a/examples/mesh/incompressible/2d_concentric_cylinders_rad20.exo b/examples/mesh/incompressible/2d_concentric_cylinders_rad20.exo deleted file mode 100644 index 40497c4..0000000 --- a/examples/mesh/incompressible/2d_concentric_cylinders_rad20.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e3eb195ca6df5da1da1abd5447719dc486e92322b02eeae37059e14e379a1ebe -size 115800 diff --git a/examples/mesh/incompressible/2d_concentric_cylinders_rad40.exo b/examples/mesh/incompressible/2d_concentric_cylinders_rad40.exo deleted file mode 100644 index 322f85e..0000000 --- a/examples/mesh/incompressible/2d_concentric_cylinders_rad40.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c09ec0c85aff8fedbae871b30076ea75c8d064688d94fab41975225b31134a92 -size 440760 diff --git a/examples/mesh/incompressible/2d_concentric_cylinders_rad80.exo b/examples/mesh/incompressible/2d_concentric_cylinders_rad80.exo deleted file mode 100644 index 75b6096..0000000 --- a/examples/mesh/incompressible/2d_concentric_cylinders_rad80.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:78adbb59b0d2be47ade1464ef08a18451aa8df44754c7c5d1cf3e6ccb1e96eb4 -size 1724280 diff --git a/examples/mesh/incompressible/2d_cyclinder_vertex_quad.exo b/examples/mesh/incompressible/2d_cyclinder_vertex_quad.exo deleted file mode 100644 index f5045b2..0000000 --- a/examples/mesh/incompressible/2d_cyclinder_vertex_quad.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27835ca20522b75584d300c8d2747a801430a8c118d91165279c87c1d556539b -size 1335072 diff --git a/examples/mesh/incompressible/2d_tee_junction.exo b/examples/mesh/incompressible/2d_tee_junction.exo deleted file mode 100644 index b1608fa..0000000 --- a/examples/mesh/incompressible/2d_tee_junction.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:130df07f9f3fcd2cfdee171b034e60fddddd0a95690b38a2eb9ae6bacc8f3a88 -size 536206 diff --git a/examples/mesh/incompressible/bluntplate_square.exo b/examples/mesh/incompressible/bluntplate_square.exo deleted file mode 100644 index f5fd934..0000000 --- a/examples/mesh/incompressible/bluntplate_square.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:65bec41d3dbcc6fbf1a61660179ffb2d1196b3a2b1e3452e45f0630dea609b63 -size 2182090 diff --git a/examples/mesh/incompressible/half_turbulent_channel_mesh_one.exo b/examples/mesh/incompressible/half_turbulent_channel_mesh_one.exo deleted file mode 100644 index 9099d06..0000000 --- a/examples/mesh/incompressible/half_turbulent_channel_mesh_one.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2e8de4e176d80387ddbaf3517eb07dc3d896b430cc0752d84ad64ee28626f04 -size 262423 diff --git a/examples/mesh/incompressible/pipe_hex.exo b/examples/mesh/incompressible/pipe_hex.exo deleted file mode 100644 index ec5992f..0000000 --- a/examples/mesh/incompressible/pipe_hex.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d0666e8146b5dbc0600039a8795534d4396a368486068b910612adef16bf652d -size 642600 diff --git a/examples/mesh/incompressible/turbulent_channel_mesh_one.exo b/examples/mesh/incompressible/turbulent_channel_mesh_one.exo deleted file mode 100644 index 7b92bfa..0000000 --- a/examples/mesh/incompressible/turbulent_channel_mesh_one.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9dc06435d9af34f80c265f7de6ba87094ffcd81451ccff75fda44a2703e1dcda -size 485823 diff --git a/examples/mesh/test_mesh_manager.exo b/examples/mesh/test_mesh_manager.exo deleted file mode 100644 index 3d42b52..0000000 --- a/examples/mesh/test_mesh_manager.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2b6554f737d7ec75d77972f526fd524fa105192b689d59b5afcac37aa90b1d02 -size 163997 diff --git a/examples/post_processing/lambda-2-contour.py b/examples/post_processing/lambda-2-contour.py deleted file mode 100644 index 620c698..0000000 --- a/examples/post_processing/lambda-2-contour.py +++ /dev/null @@ -1,85 +0,0 @@ -# Script for creating a lambda-2 contour for LES turbulence visualization. -# Also calculates the vorticity field for coloring the isosurface. - -# For details on the lambda-2 criterion, see pp. 76-77 in: -# J. Jeong and F. Hussain, “On the identification of a vortex,” -# Journal of Fluid Mechanics, vol. 285, pp. 69–94, 1995, -# doi: 10.1017/S0022112095000462. - -# Script generated using ParaView version 5.11.0 - -################################################################################ - -# USAGE: -# 1. Open a ParaView instance and load desired solution file -# 2. Import this script to Paraview by going to Macros -> Import new macro... -# and then selecting this script in the browser. -# 3. Run the macro by going to Macros -> lambda-2-contour -# 4. You can then view a contour of the lambda-2 parameter in the pipeline -# browser, and optionally color by the vorticity magnitude. - -################################################################################ - -# Import the simple module from ParaView -from paraview.simple import * -# Disable automatic camera reset on 'Show' -paraview.simple._DisableFirstRenderCameraReset() - -# Find source file -solutionFile = FindSource('*.exo') - -# Merge blocks to allow manipulation of .exo data -mergeBlocks1 = MergeBlocks(registrationName='MergeBlocks1', Input=solutionFile) - -UpdatePipeline(time=100.0, proxy=mergeBlocks1) - -# Merge velocity components into vector -mergeVectorComponents1 = MergeVectorComponents( - registrationName='MergeVectorComponents1', Input=mergeBlocks1) -mergeVectorComponents1.XArray = 'velocity_0' -mergeVectorComponents1.YArray = 'velocity_1' -mergeVectorComponents1.ZArray = 'velocity_2' -mergeVectorComponents1.OutputVectorName = 'velocity' - -UpdatePipeline(time=100.0, proxy=mergeVectorComponents1) - -# Use a programmable filter to calculate lambda2 criterion -programmableFilter1 = ProgrammableFilter( - registrationName='ProgrammableFilter1', Input=mergeVectorComponents1) -programmableFilter1.Script = """import numpy as np - -vvector = inputs[0].PointData['velocity'] - -vstrain = strain(vvector) -vskew = gradient(vvector) - vstrain - -aaa = matmul(vstrain, vstrain) + matmul(vskew, vskew) - -lambdas = np.linalg.eigvals(aaa) -lambdas = np.real(lambdas ) -lambda2 = sort(lambdas)[:,1] - -output.DeepCopy(inputs[0].VTKObject) -output.PointData.append(lambda2, 'lambda2')""" -programmableFilter1.RequestInformationScript = '' -programmableFilter1.RequestUpdateExtentScript = '' -programmableFilter1.PythonPath = '' - -UpdatePipeline(time=100.0, proxy=programmableFilter1) - -# Calculate vorticity field -calculator1 = Calculator(registrationName='Calculator1', - Input=programmableFilter1) -calculator1.AttributeType = 'Cell Data' -calculator1.ResultArrayName = 'vorticity' -calculator1.Function = '(GRAD_velocity_2Y - GRAD_velocity_1Z) * iHat + (GRAD_velocity_0Z - GRAD_velocity_2X) * jHat + (GRAD_velocity_1X - GRAD_velocity_0Y) * kHat' - -UpdatePipeline(time=100.0, proxy=calculator1) - -# Contour by lambda2 = 0 -contour1 = Contour(registrationName='Contour1', Input=calculator1) -contour1.ContourBy = ['POINTS', 'lambda2'] -contour1.Isosurfaces = [0.0] -contour1.PointMergeMethod = 'Uniform Binning' - -UpdatePipeline(time=100.0, proxy=contour1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 313c7ec..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,515 +0,0 @@ -if(VertexCFD_ENABLE_TESTING) - add_subdirectory(test_harness) -endif() -add_subdirectory(utils) - -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) - -set(VERTEXCFD_EQUATIONSET_HEADERS - equation_sets/VertexCFD_EquationSet_Factory.hpp - equation_sets/VertexCFD_EquationSet_Heat.hpp - equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.hpp - ) - -set(VERTEXCFD_EQUATIONSET_SOURCES - equation_sets/VertexCFD_EquationSet_Heat.cpp - equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.cpp - ) - -set(VERTEXCFD_CLOSUREMODEL_HEADERS - closure_models/VertexCFD_ClosureModelFactory.hpp - closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp - closure_models/VertexCFD_Closure_ElementLength.hpp - closure_models/VertexCFD_Closure_MeasureElementLength.hpp - closure_models/VertexCFD_Closure_SingularValueElementLength.hpp - closure_models/VertexCFD_Closure_MetricTensor.hpp - closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp - closure_models/VertexCFD_Closure_ExternalFields.hpp - closure_models/VertexCFD_Closure_ExternalMagneticField.hpp - closure_models/VertexCFD_Closure_MethodManufacturedSolution.hpp - closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource.hpp - closure_models/VertexCFD_Closure_WallDistance.hpp - closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp - closure_models/VertexCFD_Closure_ConstantScalarField.hpp - incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp - ) - -set(VERTEXCFD_CLOSUREMODEL_SOURCES - closure_models/VertexCFD_Closure_ElementLength.cpp - closure_models/VertexCFD_Closure_MeasureElementLength.cpp - closure_models/VertexCFD_Closure_SingularValueElementLength.cpp - closure_models/VertexCFD_Closure_MetricTensor.cpp - closure_models/VertexCFD_Closure_MetricTensorElementLength.cpp - closure_models/VertexCFD_Closure_ExternalFields.cpp - closure_models/VertexCFD_Closure_ExternalMagneticField.cpp - closure_models/VertexCFD_Closure_MethodManufacturedSolution.cpp - closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Residual.cpp - closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Jacobian.cpp - closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Tangent.cpp - closure_models/VertexCFD_Closure_WallDistance.cpp - closure_models/VertexCFD_Closure_WallDistance_impl.hpp - closure_models/VertexCFD_Closure_VectorFieldDivergence.cpp - closure_models/VertexCFD_Closure_ConstantScalarField.cpp - closure_models/VertexCFD_ClosureModelFactory_Residual2d.cpp - closure_models/VertexCFD_ClosureModelFactory_Residual3d.cpp - closure_models/VertexCFD_ClosureModelFactory_Jacobian2d.cpp - closure_models/VertexCFD_ClosureModelFactory_Jacobian3d.cpp - closure_models/VertexCFD_ClosureModelFactory_Tangent2d.cpp - closure_models/VertexCFD_ClosureModelFactory_Tangent3d.cpp - incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual2d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual3d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian2d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian3d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent2d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent3d.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.cpp - incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.cpp - turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual2d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual3d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian2d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian3d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent2d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent3d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.cpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.cpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.cpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.cpp - induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual2d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual3d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian2d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian3d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent2d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent3d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.cpp - full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual2d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual3d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian2d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian3d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent2d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent3d.cpp - ) -if(Panzer_BUILD_HESSIAN_SUPPORT) - list(APPEND VERTEXCFD_CLOSUREMODEL_SOURCES - closure_models/VertexCFD_ClosureModelFactory_Hessian2d.cpp - closure_models/VertexCFD_ClosureModelFactory_Hessian3d.cpp - closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Hessian.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian2d.cpp - incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian3d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian2d.cpp - turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian3d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian2d.cpp - induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian3d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian2d.cpp - full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian3d.cpp - ) -endif() - -set(VERTEXCFD_BOUNDARYCONDITION_HEADERS - boundary_conditions/VertexCFD_BCStrategy_Factory.hpp - boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.hpp - boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp - boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS.hpp - boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.hpp - boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp - boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp - boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp - boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp - incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp - turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp - ) - -set(VERTEXCFD_BOUNDARYCONDITION_SOURCES - boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.cpp - boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.cpp - boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.cpp - boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.cpp - boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.cpp - boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.cpp - incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.cpp - incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.cpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.cpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.cpp - induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.cpp - turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.cpp - turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.cpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.cpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.cpp - full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.cpp - ) - -set(VERTEXCFD_INITIALCONDITION_HEADERS - initial_conditions/VertexCFD_InitialConditionFactory.hpp - initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp - initial_conditions/VertexCFD_InitialCondition_Circle.hpp - initial_conditions/VertexCFD_InitialCondition_Constant.hpp - initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp - initial_conditions/VertexCFD_InitialCondition_Step.hpp - initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp - initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp - full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp - full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp - full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp - ) - -set(VERTEXCFD_INITIALCONDITION_SOURCES - initial_conditions/VertexCFD_InitialConditionFactory.cpp - initial_conditions/VertexCFD_InitialCondition_Circle.cpp - initial_conditions/VertexCFD_InitialCondition_Constant.cpp - initial_conditions/VertexCFD_InitialCondition_Gaussian.cpp - initial_conditions/VertexCFD_InitialCondition_Step.cpp - initial_conditions/VertexCFD_InitialCondition_InverseGaussian.cpp - initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.cpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.cpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.cpp - incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.cpp - full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.cpp - full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.cpp - full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.cpp - ) - -set(VERTEXCFD_OBSERVER_HEADERS - observers/VertexCFD_NOXObserver_IterationOutput.hpp - observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp - observers/VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp - observers/VertexCFD_TempusObserver_IterationOutput.hpp - observers/VertexCFD_TempusObserver_IterationOutput_impl.hpp - observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp - observers/VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp - observers/VertexCFD_TempusObserver_WriteMatrix.hpp - observers/VertexCFD_TempusObserver_WriteMatrix_impl.hpp - observers/VertexCFD_TempusObserver_WriteRestart.hpp - observers/VertexCFD_TempusObserver_WriteRestart_impl.hpp - observers/VertexCFD_TempusObserver_WriteToExodus.hpp - observers/VertexCFD_TempusObserver_WriteToExodus_impl.hpp - observers/VertexCFD_Compute_ErrorNorms.hpp - observers/VertexCFD_Compute_ErrorNorms_impl.hpp - observers/VertexCFD_Compute_Volume.hpp - observers/VertexCFD_Compute_Volume_impl.hpp - ) - -set(VERTEXCFD_OBSERVER_SOURCES - observers/VertexCFD_NOXObserver_IterationOutput.cpp - ) - -set(VERTEXCFD_PARAMETER_HEADERS - parameters/VertexCFD_GeneralScalarParameter.hpp - parameters/VertexCFD_GeneralScalarParameterInput.hpp - parameters/VertexCFD_ParameterDatabase.hpp - parameters/VertexCFD_ScalarParameter.hpp - parameters/VertexCFD_ScalarParameterEvaluator.hpp - parameters/VertexCFD_ScalarParameterInput.hpp - parameters/VertexCFD_ScalarParameterManager.hpp - parameters/VertexCFD_ScalarParameterObserver.hpp - ) - -set(VERTEXCFD_PARAMETER_SOURCES - parameters/VertexCFD_GeneralScalarParameter.cpp - parameters/VertexCFD_GeneralScalarParameterInput.cpp - parameters/VertexCFD_ParameterDatabase.cpp - parameters/VertexCFD_ScalarParameter.cpp - parameters/VertexCFD_ScalarParameterEvaluator.cpp - parameters/VertexCFD_ScalarParameterInput.cpp - parameters/VertexCFD_ScalarParameterManager.cpp - parameters/VertexCFD_ScalarParameterObserver.cpp - ) - -set(VERTEXCFD_RESPONSE_HEADERS - responses/VertexCFD_ResponseManager.hpp - responses/VertexCFD_Response_Utils.hpp - ) - -set(VERTEXCFD_RESPONSE_SOURCES - responses/VertexCFD_ResponseManager.cpp - responses/VertexCFD_Response_Utils.cpp - ) - -set(VERTEXCFD_MESH_HEADERS - mesh/VertexCFD_Mesh_ExodusWriter.hpp - mesh/VertexCFD_Mesh_Restart.hpp - mesh/VertexCFD_Mesh_StkReaderFactory.hpp - mesh/VertexCFD_Mesh_GeometryData.hpp - mesh/VertexCFD_Mesh_GeometryPrimitives.hpp - ) - -set(VERTEXCFD_MESH_SOURCES - mesh/VertexCFD_Mesh_ExodusWriter.cpp - mesh/VertexCFD_Mesh_Restart.cpp - mesh/VertexCFD_Mesh_StkReaderFactory.cpp - ) - -set(VERTEXCFD_GASPROPERTIES_HEADERS - incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp - full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp - ) - -set(VERTEXCFD_LINEARSOLVER_HEADERS - linear_solvers/VertexCFD_LinearSolvers_LocalDirectSolver.hpp - linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.hpp - linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp - linear_solvers/VertexCFD_LinearSolvers_Preconditioner.hpp - linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.hpp - ) - -set(VERTEXCFD_LINEARSOLVER_SOURCES - linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.cpp - linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.cpp - linear_solvers/VertexCFD_LinearSolvers_Preconditioner.cpp - linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.cpp - ) - -if(${VERTEXCFD_KOKKOS_DEVICE_TYPE} STREQUAL "CUDA") - list(APPEND VERTEXCFD_LINEARSOLVER_HEADERS - linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.hpp - linear_solvers/VertexCFD_LinearSolvers_CusolverNonpublic.hpp - ) - list(APPEND VERTEXCFD_LINEARSOLVER_SOURCES - linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.cpp - ) -endif() -if(VertexCFD_ENABLE_SUPERLU) - list(APPEND VERTEXCFD_LINEARSOLVER_HEADERS - linear_solvers/VertexCFD_LinearSolvers_SuperLU.hpp - ) - list(APPEND VERTEXCFD_LINEARSOLVER_SOURCES - linear_solvers/VertexCFD_LinearSolvers_SuperLU.cpp - ) -endif() - -set(VERTEXCFD_DRIVER_HEADERS - drivers/VertexCFD_ExternalFieldsManager.hpp - drivers/VertexCFD_InitialConditionManager.hpp - drivers/VertexCFD_MeshManager.hpp - drivers/VertexCFD_PhysicsManager.hpp - ) - -set(VERTEXCFD_DRIVER_SOURCES - drivers/VertexCFD_InitialConditionManager.cpp - drivers/VertexCFD_MeshManager.cpp - drivers/VertexCFD_PhysicsManager.cpp - ) - -# FIXME - use modern CMake to only include the Trilinos targets we need -include_directories(${Trilinos_INCLUDE_DIRS} ${Trilinos_TPL_INCLUDE_DIRS}) -link_directories(${Trilinos_LIBRARY_DIRS} ${Trilinos_TPL_LIBRARY_DIRS}) - -add_library(VertexCFD - ${VERTEXCFD_BOUNDARYCONDITION_SOURCES} - ${VERTEXCFD_CLOSUREMODEL_SOURCES} - ${VERTEXCFD_DRIVER_SOURCES} - ${VERTEXCFD_EQUATIONSET_SOURCES} - ${VERTEXCFD_INITIALCONDITION_SOURCES} - ${VERTEXCFD_LINEARSOLVER_SOURCES} - ${VERTEXCFD_MESH_SOURCES} - ${VERTEXCFD_PARAMETER_SOURCES} - ${VERTEXCFD_OBSERVER_SOURCES} - ${VERTEXCFD_RESPONSE_SOURCES} - ${VERTEXCFD_GASPROPERTIES_SOURCES}) - -# FIXME - use modern CMake to only include the Trilinos targets we need -target_link_libraries(VertexCFD PUBLIC - MPI::MPI_CXX - Kokkos::kokkos - ${Trilinos_LIBRARIES} - ${Trilinos_TPL_LIBRARIES} - Utils - ) - -if (CMAKE_CUDA_COMPILER) - target_link_libraries(VertexCFD PUBLIC CUDA::cusolver) -endif () -if(VertexCFD_ENABLE_SUPERLU) - target_link_libraries(VertexCFD PUBLIC SuperLU_dist::SuperLU_dist) -endif () - -target_include_directories(VertexCFD - PUBLIC - $ - $ - $) - -install(TARGETS VertexCFD - EXPORT VertexCFDTargets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -install(EXPORT VertexCFDTargets - FILE VertexCFDTargets.cmake - NAMESPACE VertexCFD:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VertexCFD) - -install(FILES - ${VERTEXCFD_EQUATIONSET_HEADERS} - ${VERTEXCFD_CLOSUREMODEL_HEADERS} - ${VERTEXCFD_DRIVER_HEADERS} - ${VERTEXCFD_BOUNDARYCONDITION_HEADERS} - ${VERTEXCFD_INITIALCONDITION_HEADERS} - ${VERTEXCFD_OBSERVER_HEADERS} - ${VERTEXCFD_MESH_HEADERS} - ${VERTEXCFD_RESPONSE_HEADERS} - ${VERTEXCFD_GASPROPERTIES_HEADERS} - ${VERTEXCFD_LINEARSOLVER_HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -include(CMakePackageConfigHelpers) - -add_executable( vertexcfd drivers/vertexcfd.cpp ) -target_link_libraries( vertexcfd PRIVATE VertexCFD ) -target_include_directories( vertexcfd PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -install(TARGETS vertexcfd DESTINATION ${CMAKE_INSTALL_DIR} ) - -if(VertexCFD_ENABLE_TESTING) - add_subdirectory(boundary_conditions/unit_test) - add_subdirectory(drivers/unit_test) - add_subdirectory(initial_conditions/unit_test) - add_subdirectory(closure_models/unit_test) - add_subdirectory(mesh/unit_test) - add_subdirectory(observers/unit_test) - add_subdirectory(parameters/unit_test) - add_subdirectory(responses/unit_test) - add_subdirectory(linear_solvers/unit_test) - # Incompressible flow - add_subdirectory(incompressible_solver/boundary_conditions/unit_test) - add_subdirectory(incompressible_solver/closure_models/unit_test) - add_subdirectory(incompressible_solver/initial_conditions/unit_test) - add_subdirectory(incompressible_solver/fluid_properties/unit_test) - # Incompressible LSVOF flow - add_subdirectory(incompressible_lsvof_solver/closure_models/unit_test) - # Turbulence models - add_subdirectory(turbulence_models/closure_models/unit_test) - add_subdirectory(turbulence_models/boundary_conditions/unit_test) - # MHD - add_subdirectory(induction_less_mhd_solver/closure_models/unit_test) - add_subdirectory(induction_less_mhd_solver/boundary_conditions/unit_test) - # Full Induction model - add_subdirectory(full_induction_mhd_solver/boundary_conditions/unit_test) - add_subdirectory(full_induction_mhd_solver/closure_models/unit_test) - add_subdirectory(full_induction_mhd_solver/initial_conditions/unit_test) - add_subdirectory(full_induction_mhd_solver/mhd_properties/unit_test) -endif() diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.cpp b/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.cpp deleted file mode 100644 index bd06967..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BCStrategy_BoundaryFluxBase.hpp" -#include "VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM( - VertexCFD::BoundaryCondition::BoundaryFluxBase) diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.hpp deleted file mode 100644 index e764c03..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_HPP -#define VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_HPP - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class BoundaryFluxBase : public panzer::BCStrategy, - public panzer::GlobalDataAcceptorDefaultImpl, - public panzer::EvaluatorWithBaseImpl -{ - public: - // Space dimension - static constexpr int num_space_dim = NumSpaceDim; - - BoundaryFluxBase(const panzer::BC& bc, - const Teuchos::RCP& global_data); - - virtual void setup(const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_data) - = 0; - - virtual void buildAndRegisterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::ClosureModelFactory_TemplateManager& factory, - const Teuchos::ParameterList& models, - const Teuchos::ParameterList& user_data) const - = 0; - - virtual void buildAndRegisterScatterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& user_data) const - = 0; - - virtual void buildAndRegisterGatherAndOrientationEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& user_data) const - = 0; - - virtual void postRegistrationSetup(typename panzer::Traits::SetupData d, - PHX::FieldManager& vm) - = 0; - - virtual void evaluateFields(typename panzer::Traits::EvalData d) = 0; - - // Local members - void initialize(const panzer::PhysicsBlock& side_pb, - std::unordered_map& dof_eq_map); - - auto getIntegrationBasis(const panzer::PhysicsBlock& side_pb, - const std::string& dof_name) const; - - void registerDOFsGradient(PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const std::string& dof_name) const; - - void registerSideNormals(PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb) const; - - void registerConvectionTypeFluxOperator( - std::pair dof_eq_pair, - std::unordered_map>& eq_vct_map, - const std::string& closure_name, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const double& multiplier) const; - - void registerPenaltyAndViscousGradientOperator( - std::pair dof_eq_pair, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_params) const; - - void registerViscousTypeFluxOperator( - std::pair dof_eq_pair, - std::unordered_map>& eq_vct_map, - const std::string closure_name, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const double& multiplier) const; - - void registerResidual( - std::pair dof_eq_pair, - std::unordered_map>& eq_vct_map, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb) const; - - void registerScatterOperator( - std::pair dof_eq_pair, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof) const; - - auto integrationRule() const { return _ir; } - - protected: - std::unordered_map bnd_prefix; - - private: - int _integration_order; - Teuchos::RCP _ir; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp deleted file mode 100644 index 162bf3d..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_BoundaryFluxBase_impl.hpp +++ /dev/null @@ -1,347 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_IMPL_HPP -#define VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_IMPL_HPP - -#include "VertexCFD_BoundaryState_ViscousGradient.hpp" -#include "VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp" -#include "VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -BoundaryFluxBase::BoundaryFluxBase( - const panzer::BC& bc, const Teuchos::RCP& global_data) - : panzer::BCStrategy(bc) - , panzer::GlobalDataAcceptorDefaultImpl(global_data) -{ - // Initialize `bnd_prefix` to use with second-order flux - bnd_prefix.insert({"BOUNDARY_", "BOUNDARY_"}); - bnd_prefix.insert({"PENALTY_BOUNDARY_", "PENALTY_"}); - bnd_prefix.insert({"SYMMETRY_BOUNDARY_", "SYMMETRY_"}); -} - -//---------------------------------------------------------------------------// -// Initialize class members -template -void BoundaryFluxBase::initialize( - const panzer::PhysicsBlock& side_pb, - std::unordered_map& /**dof_eq_map**/) -{ - // Integration rule and integration order - const auto& irules = side_pb.getIntegrationRules(); - if (irules.size() != 1) - { - throw std::runtime_error( - "BCStrategy BoundaryFluxBis too many integration rules"); - } - _integration_order = irules.begin()->second->order(); - _ir = Teuchos::rcp( - new panzer::IntegrationRule(_integration_order, side_pb.cellData())); -} - -//---------------------------------------------------------------------------// -// Get integration basis for a variable with name `dof_name`. -template -auto BoundaryFluxBase::getIntegrationBasis( - const panzer::PhysicsBlock& side_pb, const std::string& dof_name) const -{ - const auto& dof_basis_pair = side_pb.getProvidedDOFs(); - Teuchos::RCP basis; - for (auto it = dof_basis_pair.begin(); it != dof_basis_pair.end(); ++it) - { - if (it->first == dof_name) - basis = it->second; - } - return panzer::basisIRLayout(basis, *_ir); -} - -//---------------------------------------------------------------------------// -// Register degree of freedom and gradients for a variable with name `dof_name` -template -void BoundaryFluxBase::registerDOFsGradient( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const std::string& dof_name) const -{ - // Degree of freedom (DOF) - const auto basis_layout = this->getIntegrationBasis(side_pb, dof_name); - Teuchos::ParameterList dof_params; - dof_params.set("Name", dof_name); - dof_params.set>("Basis", basis_layout); - dof_params.set>("IR", _ir); - const auto dof_op - = Teuchos::rcp(new panzer::DOF(dof_params)); - this->template registerEvaluator(fm, dof_op); - - // Gradient - dof_params.set("Gradient Name", "GRAD_" + dof_name); - const auto gd_op = Teuchos::rcp( - new panzer::DOFGradient(dof_params)); - this->template registerEvaluator(fm, gd_op); -} - -//---------------------------------------------------------------------------// -// Register side nornal evaluator -template -void BoundaryFluxBase::registerSideNormals( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb) const -{ - std::stringstream normal_params_name; - normal_params_name << "Side Normal:" << side_pb.cellData().side(); - Teuchos::ParameterList normal_params(normal_params_name.str()); - normal_params.set("Name", "Side Normal"); - normal_params.set("Side ID", side_pb.cellData().side()); - normal_params.set>("IR", _ir); - normal_params.set("Normalize", true); - const auto normal_op = Teuchos::rcp( - new panzer::Normals(normal_params)); - this->template registerEvaluator(fm, normal_op); -} - -//---------------------------------------------------------------------------// -template -void BoundaryFluxBase::registerConvectionTypeFluxOperator( - std::pair dof_eq_pair, - std::unordered_map>& eq_res_map, - const std::string& closure_name, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const double& multiplier) const -{ - // Local variables - const std::string eq_name = dof_eq_pair.first; - const std::string dof_name = dof_eq_pair.second; - const std::string normal_dot_name = "Normal Dot Flux " + eq_name; - - // Register dot product evaluator - const std::string flux_name = "BOUNDARY_" + closure_name + "_FLUX_" - + eq_name; - const auto normal_dot_flux_op - = panzer::buildEvaluator_DotProduct( - normal_dot_name, *_ir, "Side Normal", flux_name); - this->template registerEvaluator(fm, normal_dot_flux_op); - - // Convective flux integral. - const auto basis_layout = this->getIntegrationBasis(side_pb, dof_name); - std::string residual_name = closure_name + "_BOUNDARY_RESIDUAL_" + eq_name; - const auto convective_flux_op = Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - residual_name, - normal_dot_name, - *basis_layout, - *_ir, - multiplier)); - this->template registerEvaluator(fm, convective_flux_op); - - // Add residual name to `eq_res_map` - eq_res_map[eq_name].push_back(residual_name); -} - -//---------------------------------------------------------------------------// -// Register closure models used for the symmetric penalty method -template -void BoundaryFluxBase::registerPenaltyAndViscousGradientOperator( - std::pair dof_eq_pair, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_params) const -{ - // Local variables - const std::string eq_name = dof_eq_pair.first; - const std::string dof_name = dof_eq_pair.second; - const auto basis_layout = this->getIntegrationBasis(side_pb, dof_name); - - // Create viscous penalty parameter. - const auto viscous_penalty_op - = Teuchos::rcp(new ViscousPenaltyParameter( - *_ir, *(basis_layout->getBasis()), dof_name, user_params)); - this->template registerEvaluator(fm, viscous_penalty_op); - - // Create boundary gradients. - const auto viscous_gradient_op = Teuchos::rcp( - new ViscousGradient(*_ir, dof_name)); - this->template registerEvaluator(fm, viscous_gradient_op); -} - -//---------------------------------------------------------------------------// -// Register Laplace-type operators -template -void BoundaryFluxBase::registerViscousTypeFluxOperator( - std::pair dof_eq_pair, - std::unordered_map>& eq_res_map, - const std::string closure_name, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const double& multiplier) const -{ - const std::string eq_name = dof_eq_pair.first; - const std::string dof_name = dof_eq_pair.second; - const auto basis_layout = this->getIntegrationBasis(side_pb, dof_name); - - // Symmetric interior penalty method residual 1. - // FIXME: The dot product evaluator is not on the device in - // Trilinos 13.0.1 - std::string normal_dot_viscous_name = "Normal Dot " + closure_name - + " Flux " + eq_name; - std::string flux_name = "BOUNDARY_" + closure_name + "_FLUX_" + eq_name; - auto normal_dot_viscous_flux_op - = panzer::buildEvaluator_DotProduct( - normal_dot_viscous_name, *_ir, "Side Normal", flux_name); - this->template registerEvaluator(fm, normal_dot_viscous_flux_op); - - std::string bnd_resid = closure_name + "_BOUNDARY_RESIDUAL_" + eq_name; - auto bnd_op = Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - bnd_resid, - normal_dot_viscous_name, - *basis_layout, - *_ir, - -multiplier)); - this->template registerEvaluator(fm, bnd_op); - eq_res_map[eq_name].push_back(bnd_resid); - - // Symmetric interior penalty method residual 2. - std::string penalty_bnd_resid = "PENALTY_BOUNDARY_RESIDUAL_" + eq_name; - auto bnd_penalty_op = Teuchos::rcp( - new Integrator::BoundaryGradBasisDotVector( - panzer::EvaluatorStyle::EVALUATES, - penalty_bnd_resid, - "PENALTY_BOUNDARY_" + closure_name + "_FLUX_" + eq_name, - *basis_layout, - *_ir, - -multiplier)); - this->template registerEvaluator(fm, bnd_penalty_op); - eq_res_map[eq_name].push_back(penalty_bnd_resid); - - // Symmetric interior penalty method residual 3. - // FIXME: The dot product evaluator is not on the device in - // Trilinos 13.0.1 - std::string normal_dot_scaled_penalty_viscous_name - = "Normal Dot Scaled Penalty " + closure_name + " Flux " + eq_name; - std::string scaled_penalty_flux_name = "SYMMETRY_BOUNDARY_" + closure_name - + "_FLUX_" + eq_name; - auto normal_dot_scaled_penalty_viscous_flux_op - = panzer::buildEvaluator_DotProduct( - normal_dot_scaled_penalty_viscous_name, - *_ir, - "Side Normal", - scaled_penalty_flux_name); - this->template registerEvaluator( - fm, normal_dot_scaled_penalty_viscous_flux_op); - - std::string scaled_penalty_bnd_resid = "SYMMETRY_BOUNDARY_RESIDUAL_" - + eq_name; - auto scaled_bnd_penalty_op = Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - scaled_penalty_bnd_resid, - normal_dot_scaled_penalty_viscous_name, - *basis_layout, - *_ir, - multiplier)); - this->template registerEvaluator(fm, scaled_bnd_penalty_op); - eq_res_map[eq_name].push_back(scaled_penalty_bnd_resid); -} - -//---------------------------------------------------------------------------// -// Register residuals collected in `eq_res_map` for each equation. -template -void BoundaryFluxBase::registerResidual( - std::pair dof_eq_pair, - std::unordered_map>& eq_res_map, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb) const -{ - // Local variables - const std::string eq_name = dof_eq_pair.first; - const std::string dof_name = dof_eq_pair.second; - const auto basis_layout = this->getIntegrationBasis(side_pb, dof_name); - - // Initialize residual vector - auto residuals = Teuchos::rcp(new std::vector); - for (auto& res : eq_res_map[eq_name]) - residuals->push_back(res); - - // Register residuals - Teuchos::ParameterList sum_params; - sum_params.set("Sum Name", "BOUNDARY_RESIDUAL_" + eq_name); - sum_params.set("Values Names", residuals); - sum_params.set("Data Layout", basis_layout->getBasis()->functional); - auto sum_op = Teuchos::rcp( - new panzer::SumStatic( - sum_params)); - this->template registerEvaluator(fm, sum_op); -} - -//---------------------------------------------------------------------------// -// Register and scatter residual for each equation -template -void BoundaryFluxBase::registerScatterOperator( - std::pair dof_eq_pair, - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof) const -{ - const auto& dof_basis_pair = side_pb.getProvidedDOFs(); - const std::string& eq_name = dof_eq_pair.first; - const std::string& dof_name = dof_eq_pair.second; - - Teuchos::RCP basis; - for (auto it = dof_basis_pair.begin(); it != dof_basis_pair.end(); ++it) - { - if (it->first == dof_name) - basis = it->second; - } - - std::string residual_name = "BOUNDARY_RESIDUAL_" + eq_name; - Teuchos::ParameterList p("Scatter: " + residual_name + " to " + eq_name); - - std::string scatter_field_name - = "Dummy Scatter: " + this->m_bc.identifier() + residual_name; - p.set("Scatter Name", scatter_field_name); - p.set("Basis", basis); - - auto residual_names = Teuchos::rcp(new std::vector); - residual_names->push_back(residual_name); - p.set("Dependent Names", residual_names); - - auto names_map = Teuchos::rcp(new std::map); - names_map->insert( - std::pair(residual_name, dof_name)); - p.set("Dependent Map", names_map); - - auto scatter_op = lof.buildScatter(p); - - this->template registerEvaluator(fm, scatter_op); - - // Require variables - auto dummy_layout = Teuchos::rcp(new PHX::MDALayout(0)); - PHX::Tag tag(scatter_field_name, dummy_layout); - fm.template requireField(tag); -} -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYCONDITION_BOUNDARYFLUXBASE_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_Factory.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_Factory.hpp deleted file mode 100644 index 52b3758..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_Factory.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_FACTORY_HPP -#define VERTEXCFD_BOUNDARYCONDITION_FACTORY_HPP - -#include "VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp" -#include "VertexCFD_BCStrategy_StrongDirichletMMS.hpp" - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -namespace Impl -{ -template class Strategy, int... Dims> -class TemplateBuilder -{ - public: - TemplateBuilder(const panzer::BC& bc, - const Teuchos::RCP& global_data) - : _bc(bc) - , _global_data(global_data) - { - } - - template - Teuchos::RCP build() const - { - Strategy* ptr - = new Strategy(_bc, _global_data); - return Teuchos::rcp(ptr); - } - - private: - const panzer::BC& _bc; - const Teuchos::RCP _global_data; -}; -} // namespace Impl - -//---------------------------------------------------------------------------// -template -class Factory : public panzer::BCStrategyFactory -{ - public: - Teuchos::RCP> - buildBCStrategy( - const panzer::BC& bc, - const Teuchos::RCP& global_data) const override - { - auto template_manager = Teuchos::rcp( - new panzer::BCStrategy_TemplateManager{}); - - const auto& bc_strategy = bc.strategy(); - - if (bc_strategy == "IncompressibleBoundaryFlux") - { - Impl::TemplateBuilder builder( - bc, global_data); - template_manager->buildObjects(builder); - } - else if (bc_strategy == "StrongDirichletMMS") - { - Impl::TemplateBuilder builder(bc, global_data); - template_manager->buildObjects(builder); - } - else - { - throw std::runtime_error("BC strategy not valid"); - } - - // Return the manager. - return template_manager; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYCONDITION_FACTORY_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.cpp b/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.cpp deleted file mode 100644 index 2ac15fd..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp" -#include "VertexCFD_BCStrategy_IncompressibleBoundaryFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleBoundaryFlux) diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp deleted file mode 100644 index d0fe34d..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_HPP -#define VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_HPP - -#include "VertexCFD_BCStrategy_BoundaryFluxBase.hpp" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleBoundaryFlux - : public BoundaryFluxBase -{ - public: - IncompressibleBoundaryFlux( - const panzer::BC& bc, - const Teuchos::RCP& global_data); - - static constexpr int num_space_dim = NumSpaceDim; - - void setup(const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_data) override; - - void buildAndRegisterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::ClosureModelFactory_TemplateManager& factory, - const Teuchos::ParameterList& models, - const Teuchos::ParameterList& user_data) const override; - - void buildAndRegisterScatterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& user_data) const override; - - void buildAndRegisterGatherAndOrientationEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& user_data) const override; - - void postRegistrationSetup(typename panzer::Traits::SetupData d, - PHX::FieldManager& vm) override; - - void evaluateFields(typename panzer::Traits::EvalData d) override; - - private: - std::unordered_map _equ_dof_ns_pair; - std::unordered_map _equ_dof_ep_pair; - std::unordered_map _equ_dof_tm_pair; - std::unordered_map _equ_dof_fim_pair; - bool _build_viscous_flux; - bool _build_temp_equ; - bool _build_ind_less_equ; - bool _turbulence_model; - bool _build_full_induction_model; - std::string _turbulence_model_name; - std::string _continuity_model_name; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux_impl.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux_impl.hpp deleted file mode 100644 index f59e5e3..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_IncompressibleBoundaryFlux_impl.hpp +++ /dev/null @@ -1,605 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_IMPL_HPP -#define VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_IMPL_HPP - -#include "VertexCFD_BoundaryState_ViscousGradient.hpp" -#include "VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp" -#include "VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp" - -#include "closure_models/VertexCFD_Closure_ExternalMagneticField.hpp" - -#include "incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp" - -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp" - -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp" -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleBoundaryFlux::IncompressibleBoundaryFlux( - const panzer::BC& bc, const Teuchos::RCP& global_data) - : BoundaryFluxBase(bc, global_data) -{ -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux::setup( - const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_data) -{ - // Viscous flux boolean. - _build_viscous_flux = user_data.get("Build Viscous Flux"); - - // Temperature equation boolean - _build_temp_equ = user_data.isType("Build Temperature Equation") - ? user_data.get("Build Temperature Equation") - : false; - - // Electric potential equation boolean - _build_ind_less_equ - = user_data.isType("Build Inductionless MHD Equation") - ? user_data.get("Build Inductionless MHD Equation") - : false; - - // Turbulence model boolean - _turbulence_model_name - = user_data.isType("Turbulence Model") - ? user_data.get("Turbulence Model") - : "No Turbulence Model"; - _turbulence_model = _turbulence_model_name == "No Turbulence Model" ? false - : true; - - // Full induction model boolean - _build_full_induction_model - = user_data.isType("Build Full Induction Model") - ? user_data.get("Build Full Induction Model") - : false; - bool build_magn_corr = false; - if (_build_full_induction_model) - { - const auto mhd_prop_list - = user_data.sublist("Full Induction MHD Properties"); - if (mhd_prop_list.isType("Build Magnetic Correction Potential " - "Equation")) - { - build_magn_corr = mhd_prop_list.get( - "Build Magnetic Correction Potential Equation"); - } - } - - // Initialize equation names and variable names for NS equations - _equ_dof_ns_pair.insert({"continuity", "lagrange_pressure"}); - for (int d = 0; d < num_space_dim; ++d) - { - const std::string ds = std::to_string(d); - _equ_dof_ns_pair.insert({"momentum_" + ds, "velocity_" + ds}); - } - if (_build_temp_equ) - _equ_dof_ns_pair.insert({"energy", "temperature"}); - - // Initialize equation name and variable name for EP equation - if (_build_ind_less_equ) - { - _equ_dof_ep_pair.insert( - {"electric_potential_equation", "electric_potential"}); - } - - // Initialize equation name and variable name for TM equations - if (_turbulence_model) - { - if (std::string::npos - != _turbulence_model_name.find("Spalart-Allmaras")) - { - _equ_dof_tm_pair.insert( - {"spalart_allmaras_equation", "spalart_allmaras_variable"}); - } - else if (std::string::npos != _turbulence_model_name.find("K-Epsilon")) - { - _equ_dof_tm_pair.insert( - {"turb_kinetic_energy_equation", "turb_kinetic_energy"}); - _equ_dof_tm_pair.insert( - {"turb_dissipation_rate_equation", "turb_dissipation_rate"}); - } - } - - // Initialize equation names and variables for FIM - if (_build_full_induction_model) - { - for (int d = 0; d < num_space_dim; ++d) - { - const std::string ds = std::to_string(d); - _equ_dof_fim_pair.insert( - {"induction_" + ds, "induced_magnetic_field_" + ds}); - } - if (build_magn_corr) - { - _equ_dof_fim_pair.insert({"magnetic_correction_potential", - "scalar_magnetic_potential"}); - } - } - - // Initialize parent class variables (only needed with one set of - // equations) - this->initialize(side_pb, _equ_dof_ns_pair); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux::buildAndRegisterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::ClosureModelFactory_TemplateManager&, - const Teuchos::ParameterList&, - const Teuchos::ParameterList& user_data) const -{ - // Map to store residuals for each equation listed in `_equ_dof_ns_pair` - std::unordered_map> eq_vct_map; - - // Get integration rule for closure models - const auto ir = this->integrationRule(); - - // Create degree of freedom and gradients for NS equations - for (auto& pair : _equ_dof_ns_pair) - { - this->registerDOFsGradient(fm, side_pb, pair.second); - } - - // Create degree of freedom and gradients for EP equations - for (auto& pair : _equ_dof_ep_pair) - { - this->registerDOFsGradient(fm, side_pb, pair.second); - } - - // Create degree of freedom and gradients for TM equations - for (auto& pair : _equ_dof_tm_pair) - { - this->registerDOFsGradient(fm, side_pb, pair.second); - } - - // Create degree of freedom and gradients for TM equations - for (auto& pair : _equ_dof_fim_pair) - { - this->registerDOFsGradient(fm, side_pb, pair.second); - } - - // Register normals - this->registerSideNormals(fm, side_pb); - - // Create boundary state operators for NS equations and EP equation - // Get bc sublist - const auto bc_params = *(this->m_bc.params()); - - // NS equations - const auto ns_bc_sublist = bc_params.isSublist("Navier-Stokes") - ? bc_params.sublist("Navier-Stokes") - : bc_params; - auto incomp_ns_boundary_state_op - = IncompressibleBoundaryStateFactory::create(*ir, - ns_bc_sublist, - user_data); - this->template registerEvaluator(fm, incomp_ns_boundary_state_op); - - // EP equations - if (_build_ind_less_equ) - { - const auto ep_bc_sublist = bc_params.sublist("Electric Potential"); - auto ep_boundary_state_op = ElectricPotentialBoundaryStateFactory< - EvalType, - panzer::Traits, - num_space_dim>::create(*ir, ep_bc_sublist, user_data); - this->template registerEvaluator(fm, ep_boundary_state_op); - } - - // Fluid properties - Teuchos::ParameterList fluid_prop_list - = user_data.sublist("Fluid Properties"); - fluid_prop_list.set("Build Temperature Equation", _build_temp_equ); - fluid_prop_list.set("Build Inductionless MHD Equation", - _build_ind_less_equ); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // First-order flux // - - // Create boundary convective fluxes for NS equations - auto convective_flux_op = Teuchos::rcp( - new ClosureModel::IncompressibleConvectiveFlux( - *ir, fluid_prop, "BOUNDARY_", "BOUNDARY_")); - this->template registerEvaluator(fm, convective_flux_op); - - for (auto& pair : _equ_dof_ns_pair) - { - this->registerConvectionTypeFluxOperator( - pair, eq_vct_map, "CONVECTIVE", fm, side_pb, 1.0); - } - - // Create boundary cross-product flux for EP equation - if (_build_ind_less_equ) - { - // Cross product term - auto cross_product_flux_op = Teuchos::rcp( - new ClosureModel::ElectricPotentialCrossProductFlux( - *ir, fluid_prop, "BOUNDARY_", "BOUNDARY_")); - this->template registerEvaluator(fm, cross_product_flux_op); - - // External magnetic field - auto ext_magn_field_op = Teuchos::rcp( - new ClosureModel::ExternalMagneticField( - *ir, user_data)); - this->template registerEvaluator(fm, ext_magn_field_op); - - for (auto& pair : _equ_dof_ep_pair) - { - this->registerConvectionTypeFluxOperator( - pair, eq_vct_map, "ELECTRIC_POTENTIAL", fm, side_pb, 1.0); - } - } - - // Second-order flux // - - // NS equations - if (_build_viscous_flux) - { - // Register penalty and viscous gradient operators for each equation. - for (auto& pair : _equ_dof_ns_pair) - { - this->registerPenaltyAndViscousGradientOperator( - pair, fm, side_pb, user_data); - } - - // Create boundary fluxes to be used with the penalty method - for (auto& pair : this->bnd_prefix) - { - // Prefix names - const std::string flux_prefix = pair.first; - const std::string gradient_prefix = pair.second; - - auto viscous_flux_op = Teuchos::rcp( - new ClosureModel::IncompressibleViscousFlux( - *ir, - fluid_prop, - user_data, - _turbulence_model, - flux_prefix, - gradient_prefix)); - this->template registerEvaluator(fm, viscous_flux_op); - } - - // Create viscous flux integrals. - for (auto& pair : _equ_dof_ns_pair) - { - this->registerViscousTypeFluxOperator( - pair, eq_vct_map, "VISCOUS", fm, side_pb, 1.0); - } - } - - // EP equation - if (_build_ind_less_equ) - { - // Register penalty and viscous gradient operators for each equation. - for (auto& pair : _equ_dof_ep_pair) - { - this->registerPenaltyAndViscousGradientOperator( - pair, fm, side_pb, user_data); - } - - // Create boundary fluxes to be used with the penalty method - for (auto& pair : this->bnd_prefix) - { - // Prefix names - const std::string flux_prefix = pair.first; - const std::string gradient_prefix = pair.second; - - auto diffusion_flux_op = Teuchos::rcp( - new ClosureModel::ElectricPotentialDiffusionFlux( - *ir, fluid_prop, flux_prefix, gradient_prefix)); - this->template registerEvaluator(fm, diffusion_flux_op); - } - - // Create diffusion flux integral - for (auto& pair : _equ_dof_ep_pair) - { - this->registerViscousTypeFluxOperator( - pair, eq_vct_map, "ELECTRIC_POTENTIAL", fm, side_pb, 1.0); - } - } - - // Turbulence model boundary fluxes // - - // TM equation: create first-order and second-order boundary fluxes - if (_turbulence_model) - { - // Use correct parameter list based on model equation count - const auto turb_bc_params - = _equ_dof_tm_pair.empty() ? Teuchos::ParameterList() - : bc_params.sublist("Turbulence Model"); - - // Define turbulent eddy viscosity on boundaries for wall functions - for (auto& pair : this->bnd_prefix) - { - // Prefix names - const std::string flux_prefix = pair.first; - - // Create evaluator for boundary turbulent eddy viscosity - auto eddy_visc_op = Teuchos::rcp( - new TurbulenceBoundaryEddyViscosity( - *ir, turb_bc_params, flux_prefix)); - - this->template registerEvaluator(fm, eddy_visc_op); - } - - // Register diffusivity coefficients and boundary conditions for each - // turbulence model equation - const auto tm_boundary_state_op = TurbulenceBoundaryStateFactory< - EvalType, - panzer::Traits, - num_space_dim>::create(*ir, - turb_bc_params, - user_data, - _turbulence_model_name, - fluid_prop); - - for (std::size_t i = 0; i < tm_boundary_state_op.size(); ++i) - { - this->template registerEvaluator( - fm, tm_boundary_state_op[i]); - } - - // Loop over each pair of the turbulence model equation(s) - for (auto& pair_tm : _equ_dof_tm_pair) - { - Teuchos::ParameterList tm_name_list; - tm_name_list.set("Field Name", pair_tm.second); - tm_name_list.set("Equation Name", pair_tm.first); - - // Create boundary convective flux for each equation - const auto convective_flux_op = Teuchos::rcp( - new ClosureModel::IncompressibleVariableConvectiveFlux< - EvalType, - panzer::Traits, - num_space_dim>( - *ir, tm_name_list, "BOUNDARY_", "BOUNDARY_")); - this->template registerEvaluator(fm, convective_flux_op); - - BoundaryFluxBase::registerConvectionTypeFluxOperator( - pair_tm, eq_vct_map, "CONVECTIVE", fm, side_pb, 1.0); - - // Register penalty and viscous gradient operators for each - // equation. - BoundaryFluxBase:: - registerPenaltyAndViscousGradientOperator( - pair_tm, fm, side_pb, user_data); - - // Create boundary fluxes to be used with the penalty method - for (auto& pair_bnd : - BoundaryFluxBase::bnd_prefix) - { - const std::string flux_prefix = pair_bnd.first; - const std::string gradient_prefix = pair_bnd.second; - - const auto diffusion_flux_op = Teuchos::rcp( - new ClosureModel::IncompressibleVariableDiffusionFlux< - EvalType, - panzer::Traits, - NumSpaceDim>( - *ir, tm_name_list, flux_prefix, gradient_prefix)); - this->template registerEvaluator(fm, - diffusion_flux_op); - } - - // Create diffusion flux integral - BoundaryFluxBase::registerViscousTypeFluxOperator( - pair_tm, eq_vct_map, "DIFFUSION", fm, side_pb, 1.0); - } - } - - // Full Induction Model boundary fluxes - - if (_build_full_induction_model) - { - const auto full_induction_params - = user_data.sublist("Full Induction MHD Properties"); - const MHDProperties::FullInductionMHDProperties mhd_props( - full_induction_params); - - // Register boundary conditions for the induciton equations - const auto fim_boundary_state_op = FullInductionBoundaryStateFactory< - EvalType, - panzer::Traits, - num_space_dim>::create(*ir, - bc_params.sublist("Full Induction Model"), - user_data, - mhd_props); - - for (std::size_t i = 0; i < fim_boundary_state_op.size(); ++i) - { - this->template registerEvaluator( - fm, fim_boundary_state_op[i]); - } - - auto induction_flux_op = Teuchos::rcp( - new ClosureModel::InductionConvectiveFlux( - *ir, mhd_props, "BOUNDARY_", "BOUNDARY_")); - this->template registerEvaluator(fm, induction_flux_op); - - for (auto& pair_fim : _equ_dof_fim_pair) - { - BoundaryFluxBase::registerConvectionTypeFluxOperator( - pair_fim, eq_vct_map, "CONVECTIVE", fm, side_pb, 1.0); - } - - if (mhd_props.buildResistiveFlux()) - { - for (auto& pair_fim : _equ_dof_fim_pair) - { - // Register penalty and resistive gradient operators for each - // equation. - BoundaryFluxBase:: - registerPenaltyAndViscousGradientOperator( - pair_fim, fm, side_pb, user_data); - - // Create boundary fluxes to be used with the penalty method - for (auto& pair_bnd : - BoundaryFluxBase::bnd_prefix) - { - const std::string flux_prefix = pair_bnd.first; - const std::string gradient_prefix = pair_bnd.second; - - // Need total magnetic field symmetry and penalty gradients - const auto tot_magn_field_grad_op = Teuchos::rcp( - new ClosureModel::TotalMagneticFieldGradient< - EvalType, - panzer::Traits, - NumSpaceDim>(*ir, gradient_prefix)); - this->template registerEvaluator( - fm, tot_magn_field_grad_op); - - const auto resistive_flux_op = Teuchos::rcp( - new ClosureModel::InductionResistiveFlux( - *ir, mhd_props, flux_prefix, gradient_prefix)); - this->template registerEvaluator( - fm, resistive_flux_op); - } - // Create resistive flux integral - BoundaryFluxBase::registerViscousTypeFluxOperator( - pair_fim, eq_vct_map, "RESISTIVE", fm, side_pb, 1.0); - } - } - } - - // Compose total residual for NS equations - for (auto& pair : _equ_dof_ns_pair) - { - this->registerResidual(pair, eq_vct_map, fm, side_pb); - } - - // Compose total residual for EP equation - for (auto& pair : _equ_dof_ep_pair) - { - this->registerResidual(pair, eq_vct_map, fm, side_pb); - } - - // Compose total residual for TM equation - for (auto& pair : _equ_dof_tm_pair) - { - BoundaryFluxBase::registerResidual( - pair, eq_vct_map, fm, side_pb); - } - - // Compose total residual for FIM equations - for (auto& pair : _equ_dof_fim_pair) - { - BoundaryFluxBase::registerResidual( - pair, eq_vct_map, fm, side_pb); - } -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux:: - buildAndRegisterScatterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& /*user_data*/) const -{ - for (auto& pair : _equ_dof_ns_pair) - { - this->registerScatterOperator(pair, fm, side_pb, lof); - } - - for (auto& pair : _equ_dof_ep_pair) - { - this->registerScatterOperator(pair, fm, side_pb, lof); - } - - for (auto& pair : _equ_dof_tm_pair) - { - BoundaryFluxBase::registerScatterOperator( - pair, fm, side_pb, lof); - } - - for (auto& pair : _equ_dof_fim_pair) - { - BoundaryFluxBase::registerScatterOperator( - pair, fm, side_pb, lof); - } -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux:: - buildAndRegisterGatherAndOrientationEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& side_pb, - const panzer::LinearObjFactory& lof, - const Teuchos::ParameterList& user_data) const -{ - side_pb.buildAndRegisterGatherAndOrientationEvaluators(fm, lof, user_data); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux::postRegistrationSetup( - typename panzer::Traits::SetupData, PHX::FieldManager&) -{ -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBoundaryFlux::evaluateFields( - typename panzer::Traits::EvalData) -{ -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYCONDITION_INCOMPRESSIBLEBOUNDARYFLUX_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS.hpp deleted file mode 100644 index 25dfec6..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_HPP -#define VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_HPP - -#include -#include -#include -#include - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class StrongDirichletMMS - : public panzer::BCStrategy_Dirichlet_DefaultImpl -{ - public: - StrongDirichletMMS(const panzer::BC& bc, - const Teuchos::RCP& global_data); - - void setup(const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList& user_data) override; - - void buildAndRegisterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock& pb, - const panzer::ClosureModelFactory_TemplateManager& factory, - const Teuchos::ParameterList& models, - const Teuchos::ParameterList& user_data) const override; - - private: - std::vector _dofs; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#include "VertexCFD_BCStrategy_StrongDirichletMMS_impl.hpp" - -#endif // end VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_HPP diff --git a/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS_impl.hpp b/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS_impl.hpp deleted file mode 100644 index 0077d2b..0000000 --- a/src/boundary_conditions/VertexCFD_BCStrategy_StrongDirichletMMS_impl.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_IMPL_HPP -#define VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -StrongDirichletMMS::StrongDirichletMMS( - const panzer::BC& bc, const Teuchos::RCP& global_data) - : panzer::BCStrategy_Dirichlet_DefaultImpl(bc, global_data) -{ - if (this->m_bc.strategy() != "StrongDirichletMMS") - { - throw std::runtime_error("StrongDirichletMMS BC name incorrect"); - } -} - -//---------------------------------------------------------------------------// -template -void StrongDirichletMMS::setup(const panzer::PhysicsBlock& side_pb, - const Teuchos::ParameterList&) -{ - _dofs = side_pb.getProvidedDOFs(); - for (auto& dof : _dofs) - { - this->addDOF(dof.first); - this->addTarget("StrongDirichletMMS_" + dof.first, dof.first); - } -} - -//---------------------------------------------------------------------------// -template -void StrongDirichletMMS::buildAndRegisterEvaluators( - PHX::FieldManager& fm, - const panzer::PhysicsBlock&, - const panzer::ClosureModelFactory_TemplateManager&, - const Teuchos::ParameterList&, - const Teuchos::ParameterList&) const -{ - for (auto& dof : _dofs) - { - Teuchos::ParameterList p("BC MMS Strong Dirichlet"); - p.set("Source Name", dof.first); - p.set("Destination Name", "StrongDirichletMMS_" + dof.first); - p.set("Data Layout", dof.second->functional); - auto op = Teuchos::rcp(new panzer::Copy(p)); - this->template registerEvaluator(fm, op); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYCONDITION_STORNGDIRICHLETMMS_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.cpp b/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.cpp deleted file mode 100644 index f7fde9f..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_MethodManufacturedSolution.hpp" -#include "VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::MethodManufacturedSolution) diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp deleted file mode 100644 index 7cc0b8f..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_HPP -#define VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Strong boundary condition for MMS -//---------------------------------------------------------------------------// -template -class MethodManufacturedSolution - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int num_coeff = 2 * (num_space_dim + 1); - - MethodManufacturedSolution(const panzer::IntegrationRule& ir); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _boundary_lagrange_pressure; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - PHX::MDField _boundary_temperature; - - PHX::MDField - _boundary_grad_lagrange_pressure; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - int _ir_degree; - int _ir_index; - - Kokkos::Array _phi_coeff; - Kokkos::Array, num_space_dim> _vel_coeff; - Kokkos::Array _T_coeff; - - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#include "VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp" - -#endif // VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp deleted file mode 100644 index cbbba2b..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution_impl.hpp +++ /dev/null @@ -1,249 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_IMPL_HPP - -#include "utils/VertexCFD_Utils_Constants.hpp" -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -MethodManufacturedSolution::MethodManufacturedSolution( - const panzer::IntegrationRule& ir) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _ir_degree(ir.cubature_degree) -{ - // Add evaludated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - this->addEvaluatedField(_boundary_temperature); - - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - this->addEvaluatedField(_boundary_grad_temperature); - - this->setName("Boundary State Method Manufactured Solution " - + std::to_string(num_space_dim) + "D"); - - // Initialize coefficients for MMS - _phi_coeff[0] = 0.0125; - _phi_coeff[1] = 1.0; - _phi_coeff[2] = 0.25; - _phi_coeff[3] = 0.5; - _phi_coeff[4] = 0.125; - _phi_coeff[5] = 0.0; - - _vel_coeff[0][0] = 0.0125; - _vel_coeff[0][1] = 0.08; - _vel_coeff[0][2] = 0.125; - _vel_coeff[0][3] = 0.0; - _vel_coeff[0][4] = 0.125; - _vel_coeff[0][5] = 0.25; - - _vel_coeff[1][0] = 0.0375; - _vel_coeff[1][1] = 1.125; - _vel_coeff[1][2] = 0.25; - _vel_coeff[1][3] = 0.0; - _vel_coeff[1][4] = 0.375; - _vel_coeff[1][5] = 0.5; - - _T_coeff[0] = 0.0625; - _T_coeff[1] = 1.0; - _T_coeff[2] = 0.375; - _T_coeff[3] = 0.25; - _T_coeff[4] = 0.25; - _T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - _phi_coeff[6] = 0.375; - _phi_coeff[7] = 1.0; - - _vel_coeff[0][6] = 0.25; - _vel_coeff[0][7] = 0.0; - - _vel_coeff[1][6] = 0.25; - _vel_coeff[1][7] = 0.5; - - _vel_coeff[2][0] = 0.025; - _vel_coeff[2][1] = 0.0; - _vel_coeff[2][2] = 0.125; - _vel_coeff[2][3] = 1.0; - _vel_coeff[2][4] = 0.25; - _vel_coeff[2][5] = 0.25; - _vel_coeff[2][6] = 0.25; - _vel_coeff[2][7] = 0.25; - - _T_coeff[6] = 0.125; - _T_coeff[7] = 0.5; - } -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_lagrange_pressure.extent(1); - using Constants::pi; - using std::cos; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // function = B + A * sin(2*pi*f_x*(x-phi_x)) * - // sin(2*pi*f_y*(x-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - - auto set_function - = [=](const Kokkos::Array coeff, - const Kokkos::Array coords) { - double return_val = coeff[0]; - for (int i = 0; i < num_space_dim; ++i) - return_val - *= sin(2.0 * pi * coeff[2 * (i + 1)] - * (coords[i] - coeff[2 * (i + 1) + 1])); - return_val += coeff[1]; - return return_val; - }; - - // function = A * 2*pi*f_x* cos(2*pi*f_x*(x-phi_x)) * - // sin(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradX_function = - [=](const Kokkos::Array coeff, - const Kokkos::Array coords) { - double return_val - = coeff[0] * 2.0 * pi * coeff[2] - * cos(2.0 * pi * coeff[2] * (coords[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (coords[1] - coeff[5])); - if (num_space_dim == 3) - return_val *= sin(2.0 * pi * coeff[6] - * (coords[2] - coeff[7])); - return return_val; - }; - - // function = A * sin(2*pi*f_x*(x-phi_x)) * 2*pi*f_y* - // cos(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradY_function = - [=](const Kokkos::Array coeff, - const Kokkos::Array coords) { - double return_val - = coeff[0] * 2.0 * pi * coeff[4] - * sin(2.0 * pi * coeff[2] * (coords[0] - coeff[3])) - * cos(2.0 * pi * coeff[4] * (coords[1] - coeff[5])); - if (num_space_dim == 3) - return_val *= sin(2.0 * pi * coeff[6] - * (coords[2] - coeff[7])); - return return_val; - }; - - // function = A * sin(2*pi*f_x*(x-phi_x)) * sin(2*pi*f_y*(y-phi_y)) - // * 2*pi*f_Z * cos(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradZ_function = - [=](const Kokkos::Array coeff, - const Kokkos::Array coords) { - return coeff[0] * 2.0 * pi * coeff[6] - * sin(2.0 * pi * coeff[2] * (coords[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (coords[1] - coeff[5])) - * cos(2.0 * pi * coeff[6] * (coords[2] - coeff[7])); - }; - - Kokkos::Array x; - for (int dim = 0; dim < num_space_dim; ++dim) - x[dim] = _ip_coords(cell, point, dim); - - const double phi = set_function(_phi_coeff, x); - const double T = set_function(_T_coeff, x); - - _boundary_lagrange_pressure(cell, point) = phi; - Kokkos::Array vel; - for (int i = 0; i < num_space_dim; ++i) - { - _boundary_velocity[i](cell, point) - = set_function(_vel_coeff[i], x); - vel[i] = _boundary_velocity[i](cell, point); - } - _boundary_temperature(cell, point) = T; - - _boundary_grad_lagrange_pressure(cell, point, 0) - = set_gradX_function(_phi_coeff, x); - _boundary_grad_lagrange_pressure(cell, point, 1) - = set_gradY_function(_phi_coeff, x); - - _boundary_grad_velocity[0](cell, point, 0) - = set_gradX_function(_vel_coeff[0], x); - _boundary_grad_velocity[0](cell, point, 1) - = set_gradY_function(_vel_coeff[0], x); - _boundary_grad_velocity[1](cell, point, 0) - = set_gradX_function(_vel_coeff[1], x); - _boundary_grad_velocity[1](cell, point, 1) - = set_gradY_function(_vel_coeff[1], x); - - _boundary_grad_temperature(cell, point, 0) - = set_gradX_function(_T_coeff, x); - _boundary_grad_temperature(cell, point, 1) - = set_gradY_function(_T_coeff, x); - - if (num_space_dim == 3) - { - _boundary_grad_lagrange_pressure(cell, point, 2) - = set_gradZ_function(_phi_coeff, x); - - _boundary_grad_velocity[0](cell, point, 2) - = set_gradZ_function(_vel_coeff[0], x); - _boundary_grad_velocity[1](cell, point, 2) - = set_gradZ_function(_vel_coeff[1], x); - _boundary_grad_velocity[2](cell, point, 0) - = set_gradX_function(_vel_coeff[2], x); - _boundary_grad_velocity[2](cell, point, 1) - = set_gradY_function(_vel_coeff[2], x); - _boundary_grad_velocity[2](cell, point, 2) - = set_gradZ_function(_vel_coeff[2], x); - - _boundary_grad_temperature(cell, point, 2) - = set_gradZ_function(_T_coeff, x); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_METHODMANUFACTUREDSOLUTION_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.cpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.cpp deleted file mode 100644 index 32191a6..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_ViscousGradient.hpp" -#include "VertexCFD_BoundaryState_ViscousGradient_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::ViscousGradient) diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.hpp deleted file mode 100644 index 3a321f8..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_HPP -#define VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_HPP - -#include "Panzer_PureBasis.hpp" -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class ViscousGradient : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ViscousGradient(const panzer::IntegrationRule& ir, - const std::string& dof_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _grad; - PHX::MDField - _scaled_grad; - - private: - int _num_space_dim; - PHX::MDField _dof; - PHX::MDField _bnd_dof; - PHX::MDField _penalty_param; - PHX::MDField - _normals; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient_impl.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient_impl.hpp deleted file mode 100644 index a18de4a..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousGradient_impl.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_IMPL_HPP - -#include "Panzer_Workset_Utilities.hpp" -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -ViscousGradient::ViscousGradient( - const panzer::IntegrationRule& ir, const std::string& dof_name) - : _grad("PENALTY_GRAD_" + dof_name, ir.dl_vector) - , _scaled_grad("SYMMETRY_GRAD_" + dof_name, ir.dl_vector) - , _num_space_dim(ir.spatial_dimension) - , _dof(dof_name, ir.dl_scalar) - , _bnd_dof("BOUNDARY_" + dof_name, ir.dl_scalar) - , _penalty_param("viscous_penalty_parameter_" + dof_name, ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) -{ - // Add evaluated fields - this->addEvaluatedField(_grad); - this->addEvaluatedField(_scaled_grad); - - // Add dependent fields - this->addDependentField(_dof); - this->addDependentField(_bnd_dof); - this->addDependentField(_penalty_param); - this->addDependentField(_normals); - - this->setName("Boundary State Viscous Gradient " - + std::to_string(_num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ViscousGradient::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ViscousGradient::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _dof.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - const auto u = _dof(cell, point); - const auto u_bnd = _bnd_dof(cell, point); - const auto delta = _penalty_param(cell, point); - - for (int dim = 0; dim < _num_space_dim; ++dim) - { - _grad(cell, point, dim) = _normals(cell, point, dim) - * (u - u_bnd); - - _scaled_grad(cell, point, dim) = delta - * _grad(cell, point, dim); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_VISCOUSGRADIENT_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.cpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.cpp deleted file mode 100644 index 5ab277b..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp" -#include "VertexCFD_BoundaryState_ViscousPenaltyParameter_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::ViscousPenaltyParameter) diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp deleted file mode 100644 index f768e4d..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_HPP -#define VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_HPP - -#include "Panzer_PureBasis.hpp" -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class ViscousPenaltyParameter : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ViscousPenaltyParameter(const panzer::IntegrationRule& ir, - const panzer::PureBasis& basis, - const std::string& dof_name, - const Teuchos::ParameterList& user_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _penalty_param; - - private: - std::string _dof_name; - std::string _basis_name; - int _num_space_dim; - int _basis_index; - double _penalty; - - PHX::MDField - _ip_gradients; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_HPP diff --git a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter_impl.hpp b/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter_impl.hpp deleted file mode 100644 index a1d9471..0000000 --- a/src/boundary_conditions/VertexCFD_BoundaryState_ViscousPenaltyParameter_impl.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_IMPL_HPP - -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -ViscousPenaltyParameter::ViscousPenaltyParameter( - const panzer::IntegrationRule& ir, - const panzer::PureBasis& basis, - const std::string& dof_name, - const Teuchos::ParameterList& user_params) - : _penalty_param("viscous_penalty_parameter_" + dof_name, ir.dl_scalar) - , _dof_name(dof_name) - , _basis_name(basis.name()) - , _num_space_dim(ir.spatial_dimension) - , _penalty(10.0) -{ - // Add evaluated field - this->addEvaluatedField(_penalty_param); - - // Set default energy penalty - if (_dof_name == "temperature") - _penalty = 1000.0; - - // Read in specific custom penalties if present - if (user_params.isSublist("Penalty Parameters")) - { - Teuchos::ParameterList penalty_list - = user_params.sublist("Penalty Parameters"); - - if ((_dof_name == "lagrange_pressure") - && (penalty_list.isType("Continuity Equation"))) - _penalty = penalty_list.get("Continuity Equation"); - if ((_dof_name == "temperature") - && (penalty_list.isType("Energy Equation"))) - _penalty = penalty_list.get("Energy Equation"); - if ((std::string::npos != _dof_name.find("velocity")) - && (penalty_list.isType("Momentum Equation"))) - _penalty = penalty_list.get("Momentum Equation"); - } - - this->setName("Boundary State Viscous Penalty Parameter " - + std::to_string(_num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ViscousPenaltyParameter::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void ViscousPenaltyParameter::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_gradients = this->wda(workset).bases[_basis_index]->grad_basis; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ViscousPenaltyParameter::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _ip_gradients.extent(1); - const int num_point = _ip_gradients.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::sqrt; - double one_over_h = 0.0; - for (int basis = 0; basis < num_basis; ++basis) - { - double one_over_h2_basis = 0.0; - for (int dim = 0; dim < _num_space_dim; ++dim) - { - one_over_h2_basis - += _ip_gradients(cell, basis, point, dim) - * _ip_gradients(cell, basis, point, dim); - } - one_over_h += sqrt(one_over_h2_basis); - } - - _penalty_param(cell, point) = _penalty * one_over_h; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_VISCOUSPENALTYPARAMETER_IMPL_HPP diff --git a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.cpp b/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.cpp deleted file mode 100644 index 6f49146..0000000 --- a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp" -#include "VertexCFD_Integrator_BoundaryGradBasisDotVector_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::Integrator::BoundaryGradBasisDotVector) diff --git a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp b/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp deleted file mode 100644 index d977d84..0000000 --- a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_HPP -#define VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_HPP - -#include -#include - -#include -#include - -#include - -//---------------------------------------------------------------------------// -// Special boundary integration operator for the penalty term of the boundary -// flux. Only the gradient tangential to the surface of the boundary side is -// used. -// -// NOTE: The normal vectors used by this evaluator to compute the tangential -// gradient are expected to be unit normals. -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Integrator -{ -template -class BoundaryGradBasisDotVector - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - BoundaryGradBasisDotVector(const panzer::EvaluatorStyle& eval_style, - const std::string& res_name, - const std::string& flux_name, - const panzer::BasisIRLayout& basis, - const panzer::IntegrationRule& ir, - const double& multiplier = 1, - const std::vector& fm_names - = std::vector{}); - - void postRegistrationSetup(typename Traits::SetupData d, - PHX::FieldManager& fm); - - void evaluateFields(typename Traits::EvalData d); - - // Regular memory version. - template - struct FieldMultTag - { - }; - - template - KOKKOS_INLINE_FUNCTION void operator()( - const FieldMultTag& tag, - const Kokkos::TeamPolicy::member_type& team) const; - - // Shared memory version. - template - struct SharedFieldMultTag - { - }; - - template - KOKKOS_INLINE_FUNCTION void operator()( - const SharedFieldMultTag& tag, - const Kokkos::TeamPolicy::member_type& team) const; - - private: - Teuchos::RCP getValidParameters() const; - - using ScalarT = typename EvalType::ScalarT; - using scratch_view - = Kokkos::View::type, - typename PHX::exec_space::scratch_memory_space, - Kokkos::MemoryUnmanaged>; - - const panzer::EvaluatorStyle _eval_style; - double _multiplier; - std::string _basis_name; - std::size_t _basis_index; - - PHX::MDField _field; - PHX::MDField - _boundary_grad_basis; - - std::vector> - _field_mults; - Kokkos::View::type, - PHX::Device>*> - _kokkos_field_mults; - - PHX::MDField _vector; - PHX::MDField _normals; - PHX::MDField - _grad_basis; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Integrator -} // end namespace VertexCFD - -#endif // VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_HPP diff --git a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector_impl.hpp b/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector_impl.hpp deleted file mode 100644 index 2e3a5f5..0000000 --- a/src/boundary_conditions/VertexCFD_Integrator_BoundaryGradBasisDotVector_impl.hpp +++ /dev/null @@ -1,429 +0,0 @@ -#ifndef VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_IMPL_HPP -#define VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_IMPL_HPP - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace Integrator -{ -//---------------------------------------------------------------------------// -template -BoundaryGradBasisDotVector::BoundaryGradBasisDotVector( - const panzer::EvaluatorStyle& eval_style, - const std::string& res_name, - const std::string& flux_name, - const panzer::BasisIRLayout& basis, - const panzer::IntegrationRule& ir, - const double& multiplier, - const std::vector& fm_names) - : _eval_style(eval_style) - , _multiplier(multiplier) - , _basis_name(basis.name()) - , _field(res_name, basis.functional) - , _boundary_grad_basis("boundary_grad_basis", basis.basis_grad) - , _vector(flux_name, ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) -{ - if (res_name == "") - { - throw std::invalid_argument( - "Error: BoundaryGradBasisDotVector called with an empty residual " - "name."); - } - if (flux_name == "") - { - throw std::invalid_argument( - "Error: BoundaryGradBasisDotVector called with an empty flux " - "name."); - } - - if (!basis.getBasis()->supportsGrad()) - { - std::string msg = "Error: BoundaryGradBasisDotVector: Basis of type " - + basis.getBasis()->name() - + " does not support the gradient operator."; - throw std::logic_error(msg); - } - - if (_eval_style == panzer::EvaluatorStyle::CONTRIBUTES) - { - this->addContributedField(_field); - } - else - { - this->addEvaluatedField(_field); - } - - this->addEvaluatedField(_boundary_grad_basis); - - this->addDependentField(_vector); - this->addDependentField(_normals); - - // Add the dependent field multipliers and expand them into kokkos views - // so we can have an arbitrary number of them on device. - const int num_name = fm_names.size(); - _field_mults.resize(num_name); - _kokkos_field_mults - = Kokkos::View::type, - PHX::Device>*>( - "GradBasisDotVector::KokkosFieldMultipliers", num_name); - for (int i = 0; i < num_name; ++i) - { - _field_mults[i] - = PHX::MDField( - fm_names[i], ir.dl_scalar); - this->addDependentField(_field_mults[i]); - } - - std::string n("BoundaryGradBasisDotVector ("); - if (_eval_style == panzer::EvaluatorStyle::CONTRIBUTES) - { - n += "CONTRIBUTES"; - } - else - { - n += "EVALUATES"; - } - n += "): " + _field.fieldTag().name(); - this->setName(n); -} - -//---------------------------------------------------------------------------// -template -void BoundaryGradBasisDotVector::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager& /* fm */) -{ - for (std::size_t i = 0; i < _field_mults.size(); ++i) - { - _kokkos_field_mults(i) = _field_mults[i].get_static_view(); - } - PHX::Device().fence(); - - _basis_index - = panzer::getBasisIndex(_basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -template -KOKKOS_INLINE_FUNCTION void -BoundaryGradBasisDotVector::operator()( - const FieldMultTag& /* tag */, - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - - // Create the boundary gradients. - const int num_qp(_vector.extent(1)); - const int num_dim(_vector.extent(2)); - const int num_bases(_grad_basis.extent(1)); - ScalarT n_dot_grad; - for (int qp = 0; qp < num_qp; ++qp) - { - for (int basis = 0; basis < num_bases; ++basis) - { - n_dot_grad = 0.0; - for (int dim = 0; dim < num_dim; ++dim) - { - n_dot_grad += _normals(cell, qp, dim) - * _grad_basis(cell, basis, qp, dim); - } - for (int dim = 0; dim < num_dim; ++dim) - { - _boundary_grad_basis(cell, basis, qp, dim) - = _grad_basis(cell, basis, qp, dim) - - _normals(cell, qp, dim) * n_dot_grad; - } - } - } - - // Initialize the evaluated field. - if (_eval_style == panzer::EvaluatorStyle::EVALUATES) - { - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { _field(cell, basis) = 0.0; }); - } - - // Perform integration with the given number of field multipliers. - ScalarT tmp; - if (NUM_FIELD_MULT == 0) - { - for (int qp = 0; qp < num_qp; ++qp) - { - for (int dim = 0; dim < num_dim; ++dim) - { - tmp = _multiplier * _vector(cell, qp, dim); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - _field(cell, basis) - += _boundary_grad_basis(cell, basis, qp, dim) * tmp; - }); - } - } - } - else if (NUM_FIELD_MULT == 1) - { - for (int qp = 0; qp < num_qp; ++qp) - { - for (int dim = 0; dim < num_dim; ++dim) - { - tmp = _multiplier * _vector(cell, qp, dim) - * _kokkos_field_mults(0)(cell, qp); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - _field(cell, basis) - += _boundary_grad_basis(cell, basis, qp, dim) * tmp; - }); - } - } - } - else - { - const int num_field_mults(_kokkos_field_mults.extent(0)); - for (int qp = 0; qp < num_qp; ++qp) - { - ScalarT field_mults_total(1); - for (int fm = 0; fm < num_field_mults; ++fm) - field_mults_total *= _kokkos_field_mults(fm)(cell, qp); - for (int dim = 0; dim < num_dim; ++dim) - { - tmp = _multiplier * _vector(cell, qp, dim) * field_mults_total; - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - _field(cell, basis) - += _boundary_grad_basis(cell, basis, qp, dim) * tmp; - }); - } - } - } -} - -//---------------------------------------------------------------------------// -template -template -KOKKOS_INLINE_FUNCTION void -BoundaryGradBasisDotVector::operator()( - const SharedFieldMultTag& /* tag */, - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_qp = _vector.extent(1); - const int num_dim = _vector.extent(2); - const int num_bases = _grad_basis.extent(1); - const int fad_size = Kokkos::dimension_scalar(_field.get_view()); - - scratch_view tmp; - scratch_view tmp_field; - if (Sacado::IsADType::value) - { - tmp = scratch_view(team.team_shmem(), 1, fad_size); - tmp_field = scratch_view(team.team_shmem(), num_bases, fad_size); - } - else - { - tmp = scratch_view(team.team_shmem(), 1); - tmp_field = scratch_view(team.team_shmem(), num_bases); - } - - // Create the boundary gradients. - ScalarT n_dot_grad; - for (int qp = 0; qp < num_qp; ++qp) - { - for (int basis = 0; basis < num_bases; ++basis) - { - n_dot_grad = 0.0; - for (int dim = 0; dim < num_dim; ++dim) - { - n_dot_grad += _normals(cell, qp, dim) - * _grad_basis(cell, basis, qp, dim); - } - for (int dim = 0; dim < num_dim; ++dim) - { - _boundary_grad_basis(cell, basis, qp, dim) - = _grad_basis(cell, basis, qp, dim) - - _normals(cell, qp, dim) * n_dot_grad; - } - } - } - - // Initialize the evaluated field. - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { tmp_field(basis) = 0.0; }); - - // Perform integration with the given number of fields. - if (NUM_FIELD_MULT == 0) - { - for (int qp = 0; qp < num_qp; ++qp) - { - for (int dim = 0; dim < num_dim; ++dim) - { - team.team_barrier(); - tmp(0) = _multiplier * _vector(cell, qp, dim); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - tmp_field(basis) - += _boundary_grad_basis(cell, basis, qp, dim) - * tmp(0); - }); - } - } - } - else if (NUM_FIELD_MULT == 1) - { - for (int qp = 0; qp < num_qp; ++qp) - { - for (int dim = 0; dim < num_dim; ++dim) - { - team.team_barrier(); - tmp(0) = _multiplier * _vector(cell, qp, dim) - * _kokkos_field_mults(0)(cell, qp); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - tmp_field(basis) - += _boundary_grad_basis(cell, basis, qp, dim) - * tmp(0); - }); - } - } - } - else - { - const int num_field_mults(_kokkos_field_mults.extent(0)); - for (int qp = 0; qp < num_qp; ++qp) - { - ScalarT field_mults_total(1); // need shared mem here - for (int fm = 0; fm < num_field_mults; ++fm) - field_mults_total *= _kokkos_field_mults(fm)(cell, qp); - for (int dim = 0; dim < num_dim; ++dim) - { - team.team_barrier(); - tmp(0) = _multiplier * _vector(cell, qp, dim) - * field_mults_total; - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - tmp_field(basis) - += _boundary_grad_basis(cell, basis, qp, dim) - * tmp(0); - }); - } - } - } - - // Put values into target field - if (_eval_style == panzer::EvaluatorStyle::EVALUATES) - { - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { _field(cell, basis) = tmp_field(basis); }); - } - else if (_eval_style == panzer::EvaluatorStyle::CONTRIBUTES) - { - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, num_bases), - [&](const int& basis) { - _field(cell, basis) += tmp_field(basis); - }); - } -} - -//---------------------------------------------------------------------------// -template -void BoundaryGradBasisDotVector::evaluateFields( - typename Traits::EvalData workset) -{ - _grad_basis = this->wda(workset).bases[_basis_index]->weighted_grad_basis; - - bool use_shared_memory = panzer::HP::inst().useSharedMemory(); - if (use_shared_memory) - { - int bytes; - if (Sacado::IsADType::value) - { - const int fad_size = Kokkos::dimension_scalar(_field.get_view()); - bytes = scratch_view::shmem_size(1, fad_size) - + scratch_view::shmem_size(_grad_basis.extent(1), fad_size); - } - else - bytes = scratch_view::shmem_size(1) - + scratch_view::shmem_size(_grad_basis.extent(1)); - - // The following if-block is for the sake of optimization depending on - // the number of field multipliers. The parallel_fors will loop over - // the cells in the Workset and execute operator()() above. - if (_field_mults.size() == 0) - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells) - .set_scratch_size(0, Kokkos::PerTeam(bytes)); - Kokkos::parallel_for(this->getName(), policy, *this); - } - else if (_field_mults.size() == 1) - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells) - .set_scratch_size(0, Kokkos::PerTeam(bytes)); - Kokkos::parallel_for(this->getName(), policy, *this); - } - else - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells) - .set_scratch_size(0, Kokkos::PerTeam(bytes)); - Kokkos::parallel_for(this->getName(), policy, *this); - } - } - else - { - // The following if-block is for the sake of optimization depending on - // the number of field multipliers. The Kokkos::parallel_fors will - // loop over the cells in the Workset and execute operator()() above. - if (_field_mults.size() == 0) - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); - } - else if (_field_mults.size() == 1) - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); - } - else - { - auto policy - = panzer::HP::inst() - .teamPolicy, PHX::Device>( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Integrator -} // end namespace VertexCFD - -#endif // VERTEXCFD_INTEGRATOR_BOUNDARYGRADBASISDOTVECTOR_IMPL_HPP diff --git a/src/boundary_conditions/unit_test/CMakeLists.txt b/src/boundary_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index d524aa9..0000000 --- a/src/boundary_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - ViscousGradient - ViscousPenaltyParameter - MethodManufacturedSolutionBC - ) diff --git a/src/boundary_conditions/unit_test/tstMethodManufacturedSolutionBC.cpp b/src/boundary_conditions/unit_test/tstMethodManufacturedSolutionBC.cpp deleted file mode 100644 index f96d24a..0000000 --- a/src/boundary_conditions/unit_test/tstMethodManufacturedSolutionBC.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include - -#include "boundary_conditions/VertexCFD_BoundaryState_MethodManufacturedSolution.hpp" -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------//¬ -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - constexpr int num_coeff = num_space_dim * 2 + 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial coordinates for the quadrature point - Kokkos::Array x; - x[0] = 0.1; - x[1] = 0.2; - auto ip_coord_view - = test_fixture.int_values->ip_coordinates.get_static_view(); - auto ip_coord_mirror = Kokkos::create_mirror(ip_coord_view); - ip_coord_mirror(0, 0, 0) = x[0]; - ip_coord_mirror(0, 0, 1) = x[1]; - if (num_space_dim == 3) - { - x[2] = 0.3; - ip_coord_mirror(0, 0, 2) = x[2]; - } - Kokkos::deep_copy(ip_coord_view, ip_coord_mirror); - - // Create mms evaluator. - auto mms_eval = Teuchos::rcp( - new BoundaryCondition:: - MethodManufacturedSolution( - *test_fixture.ir)); - test_fixture.registerEvaluator(mms_eval); - - // Function used for the MMS - using std::cos; - using std::sin; - using VertexCFD::Constants::pi; - - // function = B + A * sin(2*pi*f_x*(x-phi_x)) * sin(2*pi*f_y*(x-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_function = [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - double val = coeff[0] * sin(2.0 * pi * coeff[2] * (x[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (x[1] - coeff[5])); - double return_val - = num_space_dim == 2 - ? val + coeff[1] - : val * sin(2.0 * pi * coeff[6] * (x[2] - coeff[7])) - + coeff[1]; - return return_val; - }; - - // function = A * 2*pi*f_x* cos(2*pi*f_x*(x-phi_x)) * - // sin(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradX_function = [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - double val = coeff[0] * 2.0 * pi * coeff[2] - * cos(2.0 * pi * coeff[2] * (x[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (x[1] - coeff[5])); - double return_val - = num_space_dim == 2 - ? val - : val * sin(2.0 * pi * coeff[6] * (x[2] - coeff[7])); - - return return_val; - }; - - // function = A * sin(2*pi*f_x*(x-phi_x)) * 2*pi*f_y* - // cos(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradY_function = [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - double val = coeff[0] * 2.0 * pi * coeff[4] - * sin(2.0 * pi * coeff[2] * (x[0] - coeff[3])) - * cos(2.0 * pi * coeff[4] * (x[1] - coeff[5])); - double return_val - = num_space_dim == 2 - ? val - : val * sin(2.0 * pi * coeff[6] * (x[2] - coeff[7])); - - return return_val; - }; - - // function = A * sin(2*pi*f_x*(x-phi_x)) * sin(2*pi*f_y*(y-phi_y)) - // * 2*pi*f_Z * cos(2*pi*f_z*(z-phi_z)) for 3D - auto set_gradZ_function = [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - return coeff[0] * 2.0 * pi * coeff[6] - * sin(2.0 * pi * coeff[2] * (x[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (x[1] - coeff[5])) - * cos(2.0 * pi * coeff[6] * (x[2] - coeff[7])); - }; - - // Coefficients to be used with the above function to compute reference - // values for each primitive variable and gradients. They are hard coded in - // the source code - Kokkos::Array phi_coeff; - Kokkos::Array, num_space_dim> vel_coeff; - Kokkos::Array T_coeff; - - phi_coeff[0] = 0.0125; - phi_coeff[1] = 1.0; - phi_coeff[2] = 0.25; - phi_coeff[3] = 0.5; - phi_coeff[4] = 0.125; - phi_coeff[5] = 0.0; - - vel_coeff[0][0] = 0.0125; - vel_coeff[0][1] = 0.08; - vel_coeff[0][2] = 0.125; - vel_coeff[0][3] = 0.0; - vel_coeff[0][4] = 0.125; - vel_coeff[0][5] = 0.25; - - vel_coeff[1][0] = 0.0375; - vel_coeff[1][1] = 1.125; - vel_coeff[1][2] = 0.25; - vel_coeff[1][3] = 0.0; - vel_coeff[1][4] = 0.375; - vel_coeff[1][5] = 0.5; - - T_coeff[0] = 0.0625; - T_coeff[1] = 1.0; - T_coeff[2] = 0.375; - T_coeff[3] = 0.25; - T_coeff[4] = 0.25; - T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - phi_coeff[6] = 0.375; - phi_coeff[7] = 1.0; - - vel_coeff[0][6] = 0.25; - vel_coeff[0][7] = 0.0; - - vel_coeff[1][6] = 0.25; - vel_coeff[1][7] = 0.5; - - vel_coeff[2][0] = 0.025; - vel_coeff[2][1] = 0.0; - vel_coeff[2][2] = 0.125; - vel_coeff[2][3] = 1.0; - vel_coeff[2][4] = 0.25; - vel_coeff[2][5] = 0.25; - vel_coeff[2][6] = 0.25; - vel_coeff[2][7] = 0.25; - - T_coeff[6] = 0.125; - T_coeff[7] = 0.5; - } - - // Add required test fields. - test_fixture.registerTestField( - mms_eval->_boundary_lagrange_pressure); - test_fixture.registerTestField(mms_eval->_boundary_temperature); - - test_fixture.registerTestField( - mms_eval->_boundary_grad_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - mms_eval->_boundary_velocity[dim]); - test_fixture.registerTestField( - mms_eval->_boundary_grad_velocity[dim]); - } - test_fixture.registerTestField( - mms_eval->_boundary_grad_temperature); - - // Evaluate MMS BC. - test_fixture.evaluate(); - - // Check the BC value for all scalars. - auto boundary_lagrange_pressure_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_lagrange_pressure); - auto boundary_velocity_0_result = test_fixture.getTestFieldData( - mms_eval->_boundary_velocity[0]); - auto boundary_velocity_1_result = test_fixture.getTestFieldData( - mms_eval->_boundary_velocity[1]); - auto boundary_temperature_result = test_fixture.getTestFieldData( - mms_eval->_boundary_temperature); - - auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_grad_lagrange_pressure); - auto boundary_grad_velocity_0_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_grad_velocity[0]); - auto boundary_grad_velocity_1_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_grad_velocity[1]); - auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_grad_temperature); - - // Compute reference values using ideal gas equation of state for pressure - // and specific total energy - const double phi_ref = set_function(phi_coeff, x); - const double u_ref = set_function(vel_coeff[0], x); - const double v_ref = set_function(vel_coeff[1], x); - const double T_ref = set_function(T_coeff, x); - Kokkos::Array vel_ref; - for (int dim = 0; dim < num_space_dim; ++dim) - vel_ref[dim] = set_function(vel_coeff[dim], x); - - const double phi_gradX_ref = set_gradX_function(phi_coeff, x); - const double phi_gradY_ref = set_gradY_function(phi_coeff, x); - const double u_gradX_ref = set_gradX_function(vel_coeff[0], x); - const double u_gradY_ref = set_gradY_function(vel_coeff[0], x); - const double v_gradX_ref = set_gradX_function(vel_coeff[1], x); - const double v_gradY_ref = set_gradY_function(vel_coeff[1], x); - const double T_gradX_ref = set_gradX_function(T_coeff, x); - const double T_gradY_ref = set_gradY_function(T_coeff, x); - - int num_point = boundary_lagrange_pressure_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(phi_ref, - fieldValue(boundary_lagrange_pressure_result, 0, qp)); - EXPECT_DOUBLE_EQ(u_ref, fieldValue(boundary_velocity_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(v_ref, fieldValue(boundary_velocity_1_result, 0, qp)); - EXPECT_DOUBLE_EQ(T_ref, fieldValue(boundary_temperature_result, 0, qp)); - - EXPECT_DOUBLE_EQ( - phi_gradX_ref, - fieldValue(boundary_grad_lagrange_pressure_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ( - phi_gradY_ref, - fieldValue(boundary_grad_lagrange_pressure_result, 0, qp, 1)); - EXPECT_DOUBLE_EQ(u_gradX_ref, - fieldValue(boundary_grad_velocity_0_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ(u_gradY_ref, - fieldValue(boundary_grad_velocity_0_result, 0, qp, 1)); - EXPECT_DOUBLE_EQ(v_gradX_ref, - fieldValue(boundary_grad_velocity_1_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ(v_gradY_ref, - fieldValue(boundary_grad_velocity_1_result, 0, qp, 1)); - EXPECT_DOUBLE_EQ( - T_gradX_ref, - fieldValue(boundary_grad_temperature_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ( - T_gradY_ref, - fieldValue(boundary_grad_temperature_result, 0, qp, 1)); - } - - if (num_space_dim == 3) - { - auto boundary_velocity_2_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_velocity[2]); - auto boundary_grad_velocity_2_result - = test_fixture.getTestFieldData( - mms_eval->_boundary_grad_velocity[2]); - - const double w_ref = set_function(vel_coeff[2], x); - const double phi_gradZ_ref = set_gradZ_function(phi_coeff, x); - const double u_gradZ_ref = set_gradZ_function(vel_coeff[0], x); - const double v_gradZ_ref = set_gradZ_function(vel_coeff[1], x); - const double w_gradX_ref = set_gradX_function(vel_coeff[2], x); - const double w_gradY_ref = set_gradY_function(vel_coeff[2], x); - const double w_gradZ_ref = set_gradZ_function(vel_coeff[2], x); - const double T_gradZ_ref = set_gradZ_function(T_coeff, x); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(w_ref, - fieldValue(boundary_velocity_2_result, 0, qp)); - - EXPECT_DOUBLE_EQ( - phi_gradZ_ref, - fieldValue(boundary_grad_lagrange_pressure_result, 0, qp, 2)); - EXPECT_DOUBLE_EQ( - u_gradZ_ref, - fieldValue(boundary_grad_velocity_0_result, 0, qp, 2)); - EXPECT_DOUBLE_EQ( - v_gradZ_ref, - fieldValue(boundary_grad_velocity_1_result, 0, qp, 2)); - EXPECT_DOUBLE_EQ( - w_gradX_ref, - fieldValue(boundary_grad_velocity_2_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ( - w_gradY_ref, - fieldValue(boundary_grad_velocity_2_result, 0, qp, 1)); - EXPECT_DOUBLE_EQ( - w_gradZ_ref, - fieldValue(boundary_grad_velocity_2_result, 0, qp, 2)); - EXPECT_DOUBLE_EQ( - T_gradZ_ref, - fieldValue(boundary_grad_temperature_result, 0, qp, 2)); - } - } -} - -//---------------------------------------------------------------------------// -// Method of Manufactured Solution -// Residual -TEST(MethodManufacturedSolutionBC2D, residual_mms_test) -{ - testEval(); -} - -// Jacobian -TEST(MethodManufacturedSolutionBC2D, jacobian_mms_test) -{ - testEval(); -} - -TEST(MethodManufacturedSolutionBC3D, residual_mms_test) -{ - testEval(); -} - -// Jacobian -TEST(MethodManufacturedSolutionBC3D, jacobian_mms_test) -{ - testEval(); -} -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/boundary_conditions/unit_test/tstViscousGradient.cpp b/src/boundary_conditions/unit_test/tstViscousGradient.cpp deleted file mode 100644 index 7e4358e..0000000 --- a/src/boundary_conditions/unit_test/tstViscousGradient.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include - -#include - -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _dof; - PHX::MDField _bnd_dof; - - PHX::MDField _penalty_param; - - PHX::MDField _normals; - - Dependencies(const panzer::IntegrationRule& ir) - : _dof("lagrange_pressure", ir.dl_scalar) - , _bnd_dof("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _penalty_param("viscous_penalty_parameter_lagrange_pressure", - ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(_dof); - this->addEvaluatedField(_bnd_dof); - this->addEvaluatedField(_penalty_param); - this->addEvaluatedField(_normals); - - this->setName("Viscous Gradient Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "viscous gradient test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _dof.extent(1); - const int num_space_dim = _normals.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - // Set scalar variables - _dof(c, qp) = 0.1 * (qp + 1); - _bnd_dof(c, qp) = 1.2 * (qp + 1); - _penalty_param(c, qp) = 2.1 * (qp + 1); - - // Set normal vector - for (int dim = 0; dim < num_space_dim; ++dim) - { - _normals(c, qp, dim) = (dim + 1) * (dim * num_point + 1) * 0.2 - * (qp + 1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_space_dim) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create dependencies - auto dep_eval = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create viscous_gradient evaluator. - const std::string& dof_name = "lagrange_pressure"; - auto viscous_gradient_eval = Teuchos::rcp( - new BoundaryCondition::ViscousGradient( - *test_fixture.ir, dof_name)); - test_fixture.registerEvaluator(viscous_gradient_eval); - - // Add required test fields. - test_fixture.registerTestField(viscous_gradient_eval->_grad); - test_fixture.registerTestField( - viscous_gradient_eval->_scaled_grad); - - // Evaluate viscous_gradient. - test_fixture.evaluate(); - - // Check viscous_gradient - auto boundary_grad_result = test_fixture.getTestFieldData( - viscous_gradient_eval->_grad); - auto boundary_scaled_grad_result = test_fixture.getTestFieldData( - viscous_gradient_eval->_scaled_grad); - - int num_point = boundary_grad_result.extent(1); - - // Loop over quadrature points and mesh dimension - for (int qp = 0; qp < num_point; ++qp) - { - // Initialize variables to calculate reference values - const double u = 0.1 * (qp + 1); - const double u_bnd = 1.2 * (qp + 1); - const double delta = 2.1 * (qp + 1); - - // Compare to reference values - for (int dim = 0; dim < num_space_dim; ++dim) - { - const double n = (dim + 1) * (dim * num_point + 1) * 0.2 * (qp + 1); - EXPECT_DOUBLE_EQ(n * (u - u_bnd), - fieldValue(boundary_grad_result, 0, qp, dim)); - EXPECT_DOUBLE_EQ( - delta * n * (u - u_bnd), - fieldValue(boundary_scaled_grad_result, 0, qp, dim)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D: viscousGradient residual -TEST(ViscousGradient2D, residual_viscous_gradient_test) -{ - testEval(2); -} - -// 2-D: viscousGradient jacobian -TEST(ViscousGradient2D, jacobian_viscous_gradient_test) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D: viscousGradient residual -TEST(ViscousGradient3D, residual_viscous_gradient_test) -{ - testEval(3); -} - -// 3-D: viscousGradient jacobian -TEST(ViscousGradient3D, jacobian_viscous_gradient_test) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/boundary_conditions/unit_test/tstViscousPenaltyParameter.cpp b/src/boundary_conditions/unit_test/tstViscousPenaltyParameter.cpp deleted file mode 100644 index 77c7fdb..0000000 --- a/src/boundary_conditions/unit_test/tstViscousPenaltyParameter.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include - -#include - -#include -#include - -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(int num_space_dim) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Parameter list for penalty factors - Teuchos::ParameterList user_params; - user_params.sublist("Penalty Parameters").set("Energy Equation", 5.0); - - // Initial gradient of basis functions (one cell) and compute reference - // values 'h_ref' - int num_basis = test_fixture.workset->bases[0]->grad_basis.extent(1); - int num_point = test_fixture.workset->bases[0]->grad_basis.extent(2); - EXPECT_EQ((num_space_dim - 1) * 4, num_point); - std::array one_over_h_ref; - auto grad_basis_view - = test_fixture.workset->bases[0]->grad_basis.get_static_view(); - auto grad_basis_mirror = Kokkos::create_mirror(grad_basis_view); - for (int qp = 0; qp < num_point; ++qp) - { - one_over_h_ref[qp] = 0.0; - for (int basis = 0; basis < num_basis; basis++) - { - double one_over_h2_ref_basis = 0.0; - for (int dim = 0; dim < num_space_dim; dim++) - { - const double one_over_h2_ref_basis_dim - = 0.5 * (basis + 1) * (dim + 1 + num_space_dim) * (qp + 1); - grad_basis_mirror(0, basis, qp, dim) - = one_over_h2_ref_basis_dim; - one_over_h2_ref_basis += one_over_h2_ref_basis_dim - * one_over_h2_ref_basis_dim; - } - - one_over_h_ref[qp] += sqrt(one_over_h2_ref_basis); - } - } - Kokkos::deep_copy(grad_basis_view, grad_basis_mirror); - - // Create viscous penalty parameter evaluator. - const std::string& dof_name = "temperature"; - auto viscous_penalty_param = Teuchos::rcp( - new BoundaryCondition::ViscousPenaltyParameter( - *test_fixture.ir, - *(test_fixture.workset->bases[0]->basis_layout->getBasis()), - dof_name, - user_params)); - test_fixture.registerEvaluator(viscous_penalty_param); - - // Add required test fields. - test_fixture.registerTestField( - viscous_penalty_param->_penalty_param); - - // Evaluate viscous penalty parameter. - test_fixture.evaluate(); - - // Check viscous penalty parameter - auto boundary_penalty_param_result - = test_fixture.getTestFieldData( - viscous_penalty_param->_penalty_param); - - int num_point_rslt = boundary_penalty_param_result.extent(1); - - // Loop over quadrature points - for (int qp = 0; qp < num_point_rslt; ++qp) - { - // Compare to reference values - EXPECT_DOUBLE_EQ(5.0 * one_over_h_ref[qp], - fieldValue(boundary_penalty_param_result, 0, qp)); - } -} - -//---------------------------------------------------------------------------// -// 2-D: viscousPenaltyParameter residual -TEST(ViscousPenaltyParameter2D, residual_viscous_penalty_parameter_test) -{ - testEval(2); -} - -// 2-D: viscousPenaltyParameter jacobian -TEST(ViscousPenaltyParameter2D, jacobian_viscous_penalty_parameter_test) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D: viscousPenaltyParameter residual -TEST(ViscousPenaltyParameter3D, residual_viscous_penalty_parameter_test) -{ - testEval(3); -} - -// 3-D: viscousPenaltyParameter jacobian -TEST(ViscousPenaltyParameter3D, jacobian_viscous_penalty_parameter_test) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/VertexCFD_ClosureModelFactory.cpp b/src/closure_models/VertexCFD_ClosureModelFactory.cpp deleted file mode 100644 index 50df56f..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM( - VertexCFD::ClosureModel::Factory) diff --git a/src/closure_models/VertexCFD_ClosureModelFactory.hpp b/src/closure_models/VertexCFD_ClosureModelFactory.hpp deleted file mode 100644 index e7d377e..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef VERTEXCFD_CLOSUREMODELFACTORY_HPP -#define VERTEXCFD_CLOSUREMODELFACTORY_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class Factory : public panzer::ClosureModelFactory -{ - public: - Teuchos::RCP>>> - buildClosureModels(const std::string& model_id, - const Teuchos::ParameterList& model_params, - const panzer::FieldLayoutLibrary& fl, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& default_params, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP& global_data, - PHX::FieldManager& fm) const override; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSUREMODELFACTORY_HPP diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Hessian2d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Hessian2d.cpp deleted file mode 100644 index ae102e7..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Hessian2d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Hessian3d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Hessian3d.cpp deleted file mode 100644 index ff3057a..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Hessian3d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian2d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian2d.cpp deleted file mode 100644 index 2d3647f..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian2d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian3d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian3d.cpp deleted file mode 100644 index 0b4bec9..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Jacobian3d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Residual2d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Residual2d.cpp deleted file mode 100644 index 70e2aa8..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Residual2d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Residual3d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Residual3d.cpp deleted file mode 100644 index 17fec5f..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Residual3d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Tangent2d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Tangent2d.cpp deleted file mode 100644 index 8803640..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Tangent2d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_Tangent3d.cpp b/src/closure_models/VertexCFD_ClosureModelFactory_Tangent3d.cpp deleted file mode 100644 index e842429..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_Tangent3d.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "VertexCFD_ClosureModelFactory.hpp" -#include "VertexCFD_ClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::Factory; diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp b/src/closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp deleted file mode 100644 index 7a4796a..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef VERTEXCFD_CLOSUREMODELFACTORY_TEMPLATEBUILDER_HPP -#define VERTEXCFD_CLOSUREMODELFACTORY_TEMPLATEBUILDER_HPP - -#include "VertexCFD_ClosureModelFactory.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class FactoryTemplateBuilder -{ - public: - template - Teuchos::RCP build() const - { - auto closure_factory = Teuchos::rcp(new Factory{}); - return Teuchos::rcp_static_cast( - closure_factory); - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSUREMODELFACTORY_TEMPLATEBUILDER_HPP diff --git a/src/closure_models/VertexCFD_ClosureModelFactory_impl.hpp b/src/closure_models/VertexCFD_ClosureModelFactory_impl.hpp deleted file mode 100644 index c47ba00..0000000 --- a/src/closure_models/VertexCFD_ClosureModelFactory_impl.hpp +++ /dev/null @@ -1,297 +0,0 @@ -#ifndef VERTEXCFD_CLOSUREMODELFACTORY_IMPL_HPP -#define VERTEXCFD_CLOSUREMODELFACTORY_IMPL_HPP - -#include "VertexCFD_Closure_ElementLength.hpp" -#include "VertexCFD_Closure_ExternalMagneticField.hpp" -#include "VertexCFD_Closure_MeasureElementLength.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolution.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolutionSource.hpp" -#include "VertexCFD_Closure_MetricTensor.hpp" -#include "VertexCFD_Closure_MetricTensorElementLength.hpp" -#include "VertexCFD_Closure_SingularValueElementLength.hpp" -#include "VertexCFD_Closure_VectorFieldDivergence.hpp" -#include "VertexCFD_Closure_WallDistance.hpp" - -#include "full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp" -#include "incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp" -#include "turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp" - -#include "utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp" - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -Teuchos::RCP>>> -Factory::buildClosureModels( - const std::string& model_id, - const Teuchos::ParameterList& model_params, - const panzer::FieldLayoutLibrary&, - const Teuchos::RCP& ir, - const Teuchos::ParameterList&, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP&, - PHX::FieldManager&) const -{ - auto evaluators = Teuchos::rcp( - new std::vector>>); - - constexpr int num_space_dim = NumSpaceDim; - - if (!model_params.isSublist(model_id)) - { - throw std::runtime_error("Closure model id not in list"); - } - - // Turbulence model parameters - TurbulenceFactory tm_factory; - bool use_turbulence_model = false; - const std::string turbulence_model_name - = user_params.isType("Turbulence Model") - ? user_params.get("Turbulence Model") - : "No Turbulence Model"; - - if (turbulence_model_name != "No Turbulence Model") - { - tm_factory.buildClosureModel( - ir, user_params, turbulence_model_name, evaluators); - use_turbulence_model = true; - } - - // Incompressible equation of state - // TODO: the following logic remains until factory model functions - // are added for the turbulence models. - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_buoyancy_source - = user_params.isType("Build Buoyancy Source") - ? user_params.get("Build Buoyancy Source") - : false; - const bool build_full_ind_equ - = user_params.isSublist("Full Induction MHD Properties") ? true : false; - const bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Buoyancy Source", build_buoyancy_source); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params( - fluid_prop_list); - - // Incompressible factory model objects - IncompressibleFactory incomp_factory; - std::string incomp_error_msg = "None"; - - // Inductionless solver factory objects - InductionlessFactory inductionless_factory; - std::string ind_less_error_msg = "None"; - - // Full induction solver factory objects - FullInductionFactory full_induction_factory; - std::string full_ind_error_msg = "None"; - - // Closure model block in XML input file - const Teuchos::ParameterList& closure_model_list - = model_params.sublist(model_id); - for (const auto& closure_model : closure_model_list) - { - bool found_model = false; - - const auto closure_name = closure_model.first; - const auto& closure_params - = Teuchos::getValue(closure_model.second); - - if (closure_params.isType("Type")) - { - const auto closure_type = closure_params.get("Type"); - - // Incompressible factory - incomp_factory.buildClosureModel(closure_type, - ir, - user_params, - closure_params, - use_turbulence_model, - found_model, - incomp_error_msg, - evaluators); - - // Full induction MHD factory - if (build_full_ind_equ) - { - full_induction_factory.buildClosureModel(closure_type, - ir, - user_params, - closure_params, - found_model, - full_ind_error_msg, - evaluators); - } - - // Inductionless MHD factory - if (build_ind_less_equ) - { - inductionless_factory.buildClosureModel(closure_type, - ir, - user_params, - closure_params, - found_model, - ind_less_error_msg, - evaluators); - } - - if (closure_type == "ExternalMagneticField") - { - auto eval = Teuchos::rcp( - new ExternalMagneticField( - *ir, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "MetricTensor") - { - auto eval = Teuchos::rcp( - new MetricTensor(*ir)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "ElementLength") - { - auto eval = Teuchos::rcp( - new ElementLength(*ir)); - evaluators->push_back(eval); - found_model = true; - } - else if (closure_type == "MetricTensorElementLength") - { - auto eval = Teuchos::rcp( - new MetricTensorElementLength( - *ir)); - evaluators->push_back(eval); - found_model = true; - } - else if (closure_type == "MeasureElementLength") - { - auto eval = Teuchos::rcp( - new MeasureElementLength(*ir)); - evaluators->push_back(eval); - found_model = true; - } - else if (closure_type == "SingularValueElementLength") - { - const auto method - = closure_params.get("Element Length Method"); - auto eval = Teuchos::rcp( - new SingularValueElementLength( - *ir, method)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "MethodManufacturedSolution") - { - auto eval = Teuchos::rcp( - new MethodManufacturedSolution(*ir)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "MethodManufacturedSolutionSource") - { - bool build_viscous_flux = false; - if (user_params.isType("Build Viscous Flux")) - { - build_viscous_flux - = user_params.get("Build Viscous Flux"); - } - auto eval = Teuchos::rcp( - new MethodManufacturedSolutionSource( - *ir, - build_viscous_flux, - incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "WallDistance") - { - auto eval = Teuchos::rcp( - new WallDistance( - *ir, - user_params.get>("MeshManage" - "r"), - closure_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (std::string::npos != closure_type.find("VectorFieldDivergence")) - { - const auto field_names - = closure_params.get("Field Names"); - std::vector tokens; - panzer::StringTokenizer(tokens, field_names, ",", true); - for (auto& field : tokens) - { - auto eval = Teuchos::rcp( - new VectorFieldDivergence( - *ir, field, closure_type)); - evaluators->push_back(eval); - } - found_model = true; - } - } - - if (!found_model) - { - std::string msg = "Closure model " + closure_name - + " failed to build.\n"; - msg += "The closure models implemented in VertexCFD are:\n"; - msg += "MeasureElementLength\n"; - msg += "MethodManufacturedSolution\n"; - msg += "MethodManufacturedSolutionSource\n"; - msg += "MetricTensor\n"; - msg += "MetricTensorElementLength\n"; - msg += "SingularValueElementLength\n"; - msg += "ThermalConductivity\n"; - msg += "VectorFieldDivergence\n"; - msg += "AbsVectorFieldDivergence\n"; - msg += "=================================\n"; - msg += "Incompressible closure models:\n"; - msg += incomp_error_msg; - msg += "=================================\n"; - msg += "Full induction MHD closure models:\n"; - msg += full_ind_error_msg; - msg += "=================================\n"; - msg += "Inductionless MHD closure models:\n"; - msg += ind_less_error_msg; - - throw std::runtime_error(msg); - } - } - - return evaluators; -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSUREMODELFACTORY_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_ConstantScalarField.cpp b/src/closure_models/VertexCFD_Closure_ConstantScalarField.cpp deleted file mode 100644 index d46d054..0000000 --- a/src/closure_models/VertexCFD_Closure_ConstantScalarField.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ConstantScalarField.hpp" -#include "VertexCFD_Closure_ConstantScalarField_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::ConstantScalarField) diff --git a/src/closure_models/VertexCFD_Closure_ConstantScalarField.hpp b/src/closure_models/VertexCFD_Closure_ConstantScalarField.hpp deleted file mode 100644 index dceb683..0000000 --- a/src/closure_models/VertexCFD_Closure_ConstantScalarField.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_HPP -#define VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Closure model to assign a constant value to a named scalar field -//---------------------------------------------------------------------------// -template -class ConstantScalarField : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ConstantScalarField(const panzer::IntegrationRule& ir, - const std::string& field_name, - const double field_value); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _scalar_field; - - private: - double _field_value; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_HPP diff --git a/src/closure_models/VertexCFD_Closure_ConstantScalarField_impl.hpp b/src/closure_models/VertexCFD_Closure_ConstantScalarField_impl.hpp deleted file mode 100644 index 16fdc5d..0000000 --- a/src/closure_models/VertexCFD_Closure_ConstantScalarField_impl.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_IMPL_HPP -#define VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ConstantScalarField::ConstantScalarField( - const panzer::IntegrationRule& ir, - const std::string& field_name, - const double field_value) - : _scalar_field(field_name, ir.dl_scalar) - , _field_value(field_value) -{ - this->addEvaluatedField(_scalar_field); - - this->setName("Constant Scalar Field \"" + field_name + "\""); -} - -//---------------------------------------------------------------------------// -template -void ConstantScalarField::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ConstantScalarField::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _scalar_field.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), - [&](const int point) { _scalar_field(cell, point) = _field_value; }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_CONSTANTSCALARFIELD_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_ElementLength.cpp b/src/closure_models/VertexCFD_Closure_ElementLength.cpp deleted file mode 100644 index 77f53aa..0000000 --- a/src/closure_models/VertexCFD_Closure_ElementLength.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ElementLength.hpp" -#include "VertexCFD_Closure_ElementLength_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::ElementLength) diff --git a/src/closure_models/VertexCFD_Closure_ElementLength.hpp b/src/closure_models/VertexCFD_Closure_ElementLength.hpp deleted file mode 100644 index 9790280..0000000 --- a/src/closure_models/VertexCFD_Closure_ElementLength.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELEMENTLENGTH_HPP -#define VERTEXCFD_CLOSURE_ELEMENTLENGTH_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Element length (dimensional) evaluator. -//---------------------------------------------------------------------------// -template -class ElementLength : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ElementLength(const panzer::IntegrationRule& ir, - const std::string& prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _element_length; - - private: - PHX::MDField - _grad_basis; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELEMENTLENGTH_HPP diff --git a/src/closure_models/VertexCFD_Closure_ElementLength_impl.hpp b/src/closure_models/VertexCFD_Closure_ElementLength_impl.hpp deleted file mode 100644 index 8af8136..0000000 --- a/src/closure_models/VertexCFD_Closure_ElementLength_impl.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELEMENTLENGTH_IMPL_HPP -#define VERTEXCFD_CLOSURE_ELEMENTLENGTH_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ElementLength::ElementLength(const panzer::IntegrationRule& ir, - const std::string& prefix) - : _element_length(prefix + "element_length", ir.dl_vector) -{ - this->addEvaluatedField(_element_length); - this->setName("Element Length"); -} - -//---------------------------------------------------------------------------// -template -void ElementLength::evaluateFields( - typename Traits::EvalData workset) -{ - // TODO don't assume a 0 basis index. - _grad_basis = this->wda(workset).bases[0]->grad_basis; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ElementLength::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _element_length.extent(1); - const int num_space_dim = _element_length.extent(2); - const int num_basis = _grad_basis.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::pow; - - for (int dim = 0; dim < num_space_dim; ++dim) - { - double grad_basis_squared = 0.0; - for (int basis = 0; basis < num_basis; ++basis) - { - grad_basis_squared - += pow(_grad_basis(cell, basis, point, dim), 2); - } - _element_length(cell, point, dim) - = pow(grad_basis_squared, -0.5); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELEMENTLENGTH_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_ExternalFields.cpp b/src/closure_models/VertexCFD_Closure_ExternalFields.cpp deleted file mode 100644 index a885cf4..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalFields.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ExternalFields.hpp" -#include "VertexCFD_Closure_ExternalFields_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::ExternalFields) diff --git a/src/closure_models/VertexCFD_Closure_ExternalFields.hpp b/src/closure_models/VertexCFD_Closure_ExternalFields.hpp deleted file mode 100644 index db5b267..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalFields.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_EXTERNALFIELDS_HPP -#define VERTEXCFD_CLOSURE_EXTERNALFIELDS_HPP - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Add external fields from another physics manager as a closure model. This -// will gather the fields and put them at the basis points. -//---------------------------------------------------------------------------// -template -class ExternalFields : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ExternalFields(const std::string& evaluator_name, - const Teuchos::RCP>& - external_fields_manager, - const std::vector& external_field_names, - const Teuchos::RCP& basis); - - void postRegistrationSetup(typename Traits::SetupData d, - PHX::FieldManager& fm); - - void evaluateFields(typename Traits::EvalData d); - - private: - int _num_field; - - public: - std::vector> - _external_fields; - - private: - Teuchos::RCP _global_indexer; - std::vector _field_ids; - Kokkos::View _ghosted_field_data; - Kokkos::View _scratch_lids; - std::vector> _scratch_offsets; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_EXTERNALFIELDS_HPP diff --git a/src/closure_models/VertexCFD_Closure_ExternalFields_impl.hpp b/src/closure_models/VertexCFD_Closure_ExternalFields_impl.hpp deleted file mode 100644 index db0b7fc..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalFields_impl.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_EXTERNALFIELDS_IMPL_HPP -#define VERTEXCFD_CLOSURE_EXTERNALFIELDS_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ExternalFields::ExternalFields( - const std::string& evaluator_name, - const Teuchos::RCP>& - external_fields_manager, - const std::vector& external_field_names, - const Teuchos::RCP& basis) - : _num_field(external_field_names.size()) - , _external_fields(_num_field) - , _global_indexer(external_fields_manager->globalIndexer()) - , _field_ids(_num_field) - , _ghosted_field_data(external_fields_manager->ghostedFieldData()) -{ - // Setup evaluator data. - for (int f = 0; f < _num_field; ++f) - { - _external_fields[f] - = PHX::MDField( - external_field_names[f], basis->functional); - this->addEvaluatedField(_external_fields[f]); - } - this->setName(evaluator_name); - - // Get the field ids. - for (int f = 0; f < _num_field; ++f) - { - _field_ids[f] = _global_indexer->getFieldNum(external_field_names[f]); - } -} - -//---------------------------------------------------------------------------// -template -void ExternalFields::postRegistrationSetup( - typename Traits::SetupData d, PHX::FieldManager&) -{ - // Setup scratch data for reading the vector data. - _scratch_offsets.resize(_num_field); - const auto& workset_0 = (*d.worksets_)[0]; - auto block_id = this->wda(workset_0).block_id; - - for (int f = 0; f < _num_field; ++f) - { - const auto& offsets - = _global_indexer->getGIDFieldOffsets(block_id, _field_ids[f]); - _scratch_offsets[f] = Kokkos::View( - "external_field_offsets", offsets.size()); - auto offsets_mirror = Kokkos::create_mirror(_scratch_offsets[f]); - for (std::size_t i = 0; i < offsets.size(); ++i) - { - offsets_mirror(i) = offsets[i]; - } - Kokkos::deep_copy(_scratch_offsets[f], offsets_mirror); - } - - _scratch_lids = Kokkos::View( - "lids", - _external_fields[0].extent(0), - _global_indexer->getElementBlockGIDCount(block_id)); -} - -//---------------------------------------------------------------------------// -template -void ExternalFields::evaluateFields(typename Traits::EvalData d) -{ - // Get the local ids. - _global_indexer->getElementLIDs(this->wda(d).cell_local_ids_k, - _scratch_lids); - - // Extract the data. - auto lids = _scratch_lids; - auto field_data = _ghosted_field_data; - for (int f = 0; f < _num_field; ++f) - { - auto offsets = _scratch_offsets[f]; - auto gather_field = _external_fields[f].get_static_view(); - Kokkos::parallel_for( - Kokkos::RangePolicy(0, d.num_cells), - KOKKOS_LAMBDA(const int cell) { - int num_basis = offsets.extent(0); - for (int basis = 0; basis < num_basis; ++basis) - { - auto offset = offsets(basis); - auto lid = lids(cell, offset); - gather_field(cell, basis) = field_data(lid); - } - }); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_EXTERNALFIELDS_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_ExternalMagneticField.cpp b/src/closure_models/VertexCFD_Closure_ExternalMagneticField.cpp deleted file mode 100644 index 35e5672..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalMagneticField.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ExternalMagneticField.hpp" -#include "VertexCFD_Closure_ExternalMagneticField_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::ExternalMagneticField) diff --git a/src/closure_models/VertexCFD_Closure_ExternalMagneticField.hpp b/src/closure_models/VertexCFD_Closure_ExternalMagneticField.hpp deleted file mode 100644 index d4f00b9..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalMagneticField.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_HPP -#define VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// External magnetic field. -//---------------------------------------------------------------------------// -template -class ExternalMagneticField : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int field_size = 3; - - ExternalMagneticField(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, field_size> - _ext_magn_field; - - private: - Kokkos::Array _ext_magn_vct; - double _toroidal_field_magn; - int _ir_degree; - int _ir_index; - - enum ExtMagnType - { - constant, - toroidal - }; - - ExtMagnType _ext_magn_type; - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_HPP diff --git a/src/closure_models/VertexCFD_Closure_ExternalMagneticField_impl.hpp b/src/closure_models/VertexCFD_Closure_ExternalMagneticField_impl.hpp deleted file mode 100644 index cd5fec6..0000000 --- a/src/closure_models/VertexCFD_Closure_ExternalMagneticField_impl.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_IMPL_HPP -#define VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ExternalMagneticField::ExternalMagneticField( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params) - : _ir_degree(ir.cubature_degree) - , _ext_magn_type(ExtMagnType::constant) -{ - // Get external magnetic vector type - if (user_params.isType("External Magnetic Field Type")) - { - const auto type_validator = Teuchos::rcp( - new Teuchos::StringToIntegralParameterEntryValidator( - Teuchos::tuple("constant", "toroidal"), - "constant")); - _ext_magn_type = type_validator->getIntegralValue( - user_params.get("External Magnetic Field Type")); - } - - // Get external magnetic vector value - if (_ext_magn_type == ExtMagnType::constant) - { - const auto ext_magn_vct = user_params.get>( - "External Magnetic Field Value"); - for (int dim = 0; dim < field_size; ++dim) - _ext_magn_vct[dim] = ext_magn_vct[dim]; - } - else if (_ext_magn_type == ExtMagnType::toroidal) - { - _toroidal_field_magn - = user_params.get("Toroidal Field Magnitude"); - } - - // Evaluated fields - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _ext_magn_field, "external_magnetic_field_"); - - this->setName("External Magnetic Field"); -} - -//---------------------------------------------------------------------------// -template -void ExternalMagneticField::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void ExternalMagneticField::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ExternalMagneticField::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _ext_magn_field[0].extent(1); - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - if (_ext_magn_type == ExtMagnType::constant) - { - for (int dim = 0; dim < field_size; ++dim) - _ext_magn_field[dim](cell, point) = _ext_magn_vct[dim]; - } - else if (_ext_magn_type == ExtMagnType::toroidal) - { - _ext_magn_field[0](cell, point) - = -_ip_coords(cell, point, 1) * _toroidal_field_magn - / (_ip_coords(cell, point, 0) * _ip_coords(cell, point, 0) - + _ip_coords(cell, point, 1) - * _ip_coords(cell, point, 1)); - - _ext_magn_field[1](cell, point) - = _ip_coords(cell, point, 0) * _toroidal_field_magn - / (_ip_coords(cell, point, 0) * _ip_coords(cell, point, 0) - + _ip_coords(cell, point, 1) - * _ip_coords(cell, point, 1)); - - _ext_magn_field[2](cell, point) = 0.0; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_EXTERNALMAGNETICFIELD_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_MeasureElementLength.cpp b/src/closure_models/VertexCFD_Closure_MeasureElementLength.cpp deleted file mode 100644 index 237e21d..0000000 --- a/src/closure_models/VertexCFD_Closure_MeasureElementLength.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MeasureElementLength.hpp" -#include "VertexCFD_Closure_MeasureElementLength_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::MeasureElementLength) diff --git a/src/closure_models/VertexCFD_Closure_MeasureElementLength.hpp b/src/closure_models/VertexCFD_Closure_MeasureElementLength.hpp deleted file mode 100644 index 52c285b..0000000 --- a/src/closure_models/VertexCFD_Closure_MeasureElementLength.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_HPP -#define VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class MeasureElementLength : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _element_length; - - MeasureElementLength(const panzer::IntegrationRule& ir, - const std::string& prefix = ""); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _cell_det; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_HPP diff --git a/src/closure_models/VertexCFD_Closure_MeasureElementLength_impl.hpp b/src/closure_models/VertexCFD_Closure_MeasureElementLength_impl.hpp deleted file mode 100644 index 2b9ece6..0000000 --- a/src/closure_models/VertexCFD_Closure_MeasureElementLength_impl.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_IMPL_HPP -#define VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_IMPL_HPP - -#include -#include - -#include -#include - -/** -This class implements the logic to calculate the size of a mesh element -from the Jacobian matrix. The implementation follows the same logic as in -MFEM -(https://mfem.github.io/doxygen/html/classmfem_1_1Mesh.html#adde50e5a10f09b877c861d8371981c0c) -**/ - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MeasureElementLength::MeasureElementLength( - const panzer::IntegrationRule& ir, const std::string& prefix) - : _element_length(prefix + "element_length", ir.dl_vector) - , _ir_degree(ir.cubature_degree) -{ - this->addEvaluatedField(_element_length); - this->setName("Measure Element Length"); -} - -//---------------------------------------------------------------------------// -template -void MeasureElementLength::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MeasureElementLength::evaluateFields( - typename Traits::EvalData workset) -{ - _cell_det = workset.int_rules[_ir_index]->jac_det; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MeasureElementLength::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _element_length.extent(1); - const int num_space_dim = _element_length.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::pow; - // Compute element size - double h = pow(_cell_det(cell, point), 1.0 / num_space_dim); - - // Set value of the element length (same value for all - // directions) - for (int i = 0; i < num_space_dim; i++) - { - _element_length(cell, point, i) = h; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_MEASUREELEMENTLENGTH_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.cpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.cpp deleted file mode 100644 index 3a32951..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MethodManufacturedSolution.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolution_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::MethodManufacturedSolution) diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.hpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.hpp deleted file mode 100644 index 83e136b..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_HPP -#define VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Assumed MMS solution to be compared to computational solution. -//---------------------------------------------------------------------------// -template -class MethodManufacturedSolution - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int num_coeff = 2 * (num_space_dim + 1); - - MethodManufacturedSolution(const panzer::IntegrationRule& ir); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, num_space_dim> - _velocity; - PHX::MDField _temperature; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; - - Kokkos::Array _phi_coeff; - Kokkos::Array, num_space_dim> _vel_coeff; - Kokkos::Array _T_coeff; -}; - -//---------------------------------------------------------------------------// - -} // namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_HPP diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource.hpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource.hpp deleted file mode 100644 index 0612e10..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_HPP -#define VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class MethodManufacturedSolutionSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int num_coeff = 2 * (num_space_dim + 1); - static constexpr int num_conserve = num_space_dim + 2; - - MethodManufacturedSolutionSource( - const panzer::IntegrationRule& ir, - const bool build_viscous_flux, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()(const int cell) const; - - template - KOKKOS_INLINE_FUNCTION T - set_function(const Kokkos::Array& coeff, - const Kokkos::Array& x) const; - - PHX::MDField _continuity_mms_source; - Kokkos::Array, - num_space_dim> - _momentum_mms_source; - PHX::MDField _energy_mms_source; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; - - Kokkos::Array _phi_coeff; - Kokkos::Array, num_space_dim> _vel_coeff; - Kokkos::Array _T_coeff; - - bool _build_viscous_flux; - double _rho; - double _nu; - double _kappa; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_HPP diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Hessian.cpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Hessian.cpp deleted file mode 100644 index 5e1e620..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Hessian.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "VertexCFD_Closure_MethodManufacturedSolutionSource.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp" - -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Jacobian.cpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Jacobian.cpp deleted file mode 100644 index 783c8a7..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Jacobian.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "VertexCFD_Closure_MethodManufacturedSolutionSource.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp" - -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Residual.cpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Residual.cpp deleted file mode 100644 index c1760ba..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Residual.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "VertexCFD_Closure_MethodManufacturedSolutionSource.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp" - -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Tangent.cpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Tangent.cpp deleted file mode 100644 index a14501f..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_Tangent.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "VertexCFD_Closure_MethodManufacturedSolutionSource.hpp" -#include "VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp" - -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; -template class VertexCFD::ClosureModel:: - MethodManufacturedSolutionSource; diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp deleted file mode 100644 index 50c62dc..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolutionSource_impl.hpp +++ /dev/null @@ -1,307 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_IMPL_HPP - -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MethodManufacturedSolutionSource:: - MethodManufacturedSolutionSource( - const panzer::IntegrationRule& ir, - const bool build_viscous_flux, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _continuity_mms_source("MMS_SOURCE_continuity", ir.dl_scalar) - , _energy_mms_source("MMS_SOURCE_energy", ir.dl_scalar) - , _ir_degree(ir.cubature_degree) - , _build_viscous_flux(build_viscous_flux) - , _rho(fluid_prop.constantDensity()) - , _nu(fluid_prop.constantKinematicViscosity()) - , _kappa(0.0) -{ - this->addEvaluatedField(_continuity_mms_source); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _momentum_mms_source, "MMS_SOURCE_momentum_"); - this->addEvaluatedField(_energy_mms_source); - - // MMS function coefficients - _phi_coeff[0] = 0.0125; - _phi_coeff[1] = 1.0; - _phi_coeff[2] = 0.25; - _phi_coeff[3] = 0.5; - _phi_coeff[4] = 0.125; - _phi_coeff[5] = 0.0; - - _vel_coeff[0][0] = 0.0125; - _vel_coeff[0][1] = 0.08; - _vel_coeff[0][2] = 0.125; - _vel_coeff[0][3] = 0.0; - _vel_coeff[0][4] = 0.125; - _vel_coeff[0][5] = 0.25; - - _vel_coeff[1][0] = 0.0375; - _vel_coeff[1][1] = 1.125; - _vel_coeff[1][2] = 0.25; - _vel_coeff[1][3] = 0.0; - _vel_coeff[1][4] = 0.375; - _vel_coeff[1][5] = 0.5; - - _T_coeff[0] = 0.0625; - _T_coeff[1] = 1.0; - _T_coeff[2] = 0.375; - _T_coeff[3] = 0.25; - _T_coeff[4] = 0.25; - _T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - _phi_coeff[6] = 0.375; - _phi_coeff[7] = 1.0; - - _vel_coeff[0][6] = 0.25; - _vel_coeff[0][7] = 0.0; - - _vel_coeff[1][6] = 0.25; - _vel_coeff[1][7] = 0.5; - - _vel_coeff[2][0] = 0.025; - _vel_coeff[2][1] = 0.0; - _vel_coeff[2][2] = 0.125; - _vel_coeff[2][3] = 1.0; - _vel_coeff[2][4] = 0.25; - _vel_coeff[2][5] = 0.25; - _vel_coeff[2][6] = 0.25; - _vel_coeff[2][7] = 0.25; - - _T_coeff[6] = 0.125; - _T_coeff[7] = 0.5; - } - - this->setName("Method of Manufactured Solution Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolutionSource:: - postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolutionSource::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - Kokkos::RangePolicy policy(0, workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -template -T MethodManufacturedSolutionSource::set_function( - const Kokkos::Array& coeff, - const Kokkos::Array& x) const -{ - using Constants::pi; - - // Function = B+ A * sin(2*pi*f_x*(x-phi_x)) * sin(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - T val = coeff[0]; - for (int i = 0; i < num_space_dim; ++i) - val *= sin(2.0 * pi * coeff[2 * (i + 1)] - * (x[i] - coeff[2 * (i + 1) + 1])); - val += coeff[1]; - return val; -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolutionSource::operator()( - const int cell) const -{ - const int num_point = _continuity_mms_source.extent(1); - using Constants::pi; - const double third = 1.0 / 3.0; - - for (int point = 0; point < num_point; ++point) - { - using diff1_type = Sacado::Fad::SFad; - using diff2_type = Sacado::Fad::SFad; - - // Create independent variables. - Kokkos::Array diff1_x; - Kokkos::Array diff2_x; - - // Assign values - for (int i = 0; i < num_space_dim; ++i) - diff1_x[i] = _ip_coords(cell, point, i); - - // Initialize derivative - for (int i = 0; i < num_space_dim; ++i) - { - diff1_x[i].diff(i, num_space_dim); - diff2_x[i] = diff1_x[i]; - } - - for (int i = 0; i < num_space_dim; ++i) - diff2_x[i].diff(i, num_space_dim); - - // Variables with second derivatives - Kokkos::Array diff2_vel; - for (int i = 0; i < num_space_dim; ++i) - diff2_vel[i] = set_function(_vel_coeff[i], diff2_x); - const diff2_type diff2_T = set_function(_T_coeff, diff2_x); - - // diff1 gradients - Kokkos::Array, num_space_dim> - diff1_grad_u; - Kokkos::Array diff1_grad_T; - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - diff1_grad_u[i][j] = diff2_vel[i].fastAccessDx(j); - - diff1_grad_T[i] = diff2_T.fastAccessDx(i); - } - - diff1_type diff1_tr_grad_u = 0.0; - for (int i = 0; i < num_space_dim; ++i) - diff1_tr_grad_u += diff1_grad_u[i][i]; - diff1_tr_grad_u *= third; - - // diff1 primitives - const diff1_type diff1_phi = set_function(_phi_coeff, diff1_x); - - Kokkos::Array diff1_vel; - for (int i = 0; i < num_space_dim; ++i) - diff1_vel[i] = diff2_vel[i].val(); - const diff1_type diff1_T = diff2_T.val(); - - ////////////////////////////// - // Convective flux - ////////////////////////////// - Kokkos::Array, num_conserve> - conv_flux; - - // Convective continuity - for (int i = 0; i < num_space_dim; ++i) - conv_flux[0][i] = diff1_vel[i]; - - // Convective momentum - for (int i = 0; i < num_space_dim; ++i) - for (int j = 0; j < num_space_dim; ++j) - { - conv_flux[i + 1][j] = diff1_vel[i] * diff1_vel[j]; - if (i == j) - conv_flux[i + 1][j] += diff1_phi; - } - - // Convective energy - for (int i = 0; i < num_space_dim; ++i) - { - conv_flux[num_conserve - 1][i] = diff1_T * diff1_vel[i]; - } - - // Total flux - Kokkos::Array, num_conserve> flux; - for (int i = 0; i < num_conserve; ++i) - for (int j = 0; j < num_space_dim; ++j) - flux[i][j] = conv_flux[i][j]; - - ////////////////////////////// - // Vicous flux - ////////////////////////////// - if (_build_viscous_flux) - { - // Compute viscous stress tensor - Kokkos::Array, num_space_dim> - diff1_tau; - for (int i = 0; i < num_space_dim; ++i) - for (int j = 0; j < num_space_dim; ++j) - { - if (i == j) - diff1_tau[i][j] - = 2.0 * _nu - * (diff1_grad_u[i][j] - diff1_tr_grad_u); - else - diff1_tau[i][j] - = _nu * (diff1_grad_u[i][j] + diff1_grad_u[j][i]); - } - - // Vicous flux - Kokkos::Array, num_conserve> - visc_flux; - - // Viscous continuity - for (int i = 0; i < num_space_dim; ++i) - visc_flux[0][i] = 0.0; - - // Viscous momentum - for (int i = 0; i < num_space_dim; ++i) - for (int j = 0; j < num_space_dim; ++j) - visc_flux[i + 1][j] = diff1_tau[i][j]; - - // Viscous energy - for (int i = 0; i < num_space_dim; ++i) - { - diff1_type viscous_work = 0.0; - for (int j = 0; j < num_space_dim; ++j) - viscous_work += diff1_vel[j] * diff1_tau[i][j]; - - visc_flux[num_conserve - 1][i] = viscous_work - + _kappa * diff1_grad_T[i]; - } - - // Total flux - for (int i = 0; i < num_conserve; ++i) - for (int j = 0; j < num_space_dim; ++j) - flux[i][j] -= visc_flux[i][j]; - } - - // Sum up flux in each direction - scalar_type continuity_sum = 0.0; - Kokkos::Array mom_sum; - for (int i = 0; i < num_space_dim; ++i) - mom_sum[i] = 0.0; - scalar_type energy_sum = 0.0; - for (int i = 0; i < num_space_dim; ++i) - { - continuity_sum += flux[0][i].fastAccessDx(i); - for (int j = 0; j < num_space_dim; ++j) - mom_sum[i] += flux[i + 1][j].fastAccessDx(j); - // FIXME: warning: iteration 2 invokes undefined behavior - // [-Waggressive-loop-optimizations] - energy_sum += flux[num_conserve - 1][i].fastAccessDx(i); - } - - _continuity_mms_source(cell, point) = continuity_sum; - for (int i = 0; i < num_space_dim; ++i) - _momentum_mms_source[i](cell, point) = mom_sum[i]; - _energy_mms_source(cell, point) = energy_sum; - } -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTIONSOURCE_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution_impl.hpp b/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution_impl.hpp deleted file mode 100644 index 4ef1a13..0000000 --- a/src/closure_models/VertexCFD_Closure_MethodManufacturedSolution_impl.hpp +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_IMPL_HPP -#define VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_IMPL_HPP - -#include "utils/VertexCFD_Utils_Constants.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include -#include -#include -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MethodManufacturedSolution::MethodManufacturedSolution( - const panzer::IntegrationRule& ir) - : _lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _temperature("Exact_temperature", ir.dl_scalar) - , _ir_degree(ir.cubature_degree) - -{ - this->addEvaluatedField(_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "Exact_velocity_"); - this->addEvaluatedField(_temperature); - - this->setName("Method of Manufactured Solution " - + std::to_string(num_space_dim) + "D"); - - _phi_coeff[0] = 0.0125; - _phi_coeff[1] = 1.0; - _phi_coeff[2] = 0.25; - _phi_coeff[3] = 0.5; - _phi_coeff[4] = 0.125; - _phi_coeff[5] = 0.0; - - _vel_coeff[0][0] = 0.0125; - _vel_coeff[0][1] = 0.08; - _vel_coeff[0][2] = 0.125; - _vel_coeff[0][3] = 0.0; - _vel_coeff[0][4] = 0.125; - _vel_coeff[0][5] = 0.25; - - _vel_coeff[1][0] = 0.0375; - _vel_coeff[1][1] = 1.125; - _vel_coeff[1][2] = 0.25; - _vel_coeff[1][3] = 0.0; - _vel_coeff[1][4] = 0.375; - _vel_coeff[1][5] = 0.5; - - _T_coeff[0] = 0.0625; - _T_coeff[1] = 1.0; - _T_coeff[2] = 0.375; - _T_coeff[3] = 0.25; - _T_coeff[4] = 0.25; - _T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - _phi_coeff[6] = 0.375; - _phi_coeff[7] = 1.0; - - _vel_coeff[0][6] = 0.25; - _vel_coeff[0][7] = 0.0; - - _vel_coeff[1][6] = 0.25; - _vel_coeff[1][7] = 0.5; - - _vel_coeff[2][0] = 0.025; - _vel_coeff[2][1] = 0.0; - _vel_coeff[2][2] = 0.125; - _vel_coeff[2][3] = 1.0; - _vel_coeff[2][4] = 0.25; - _vel_coeff[2][5] = 0.25; - _vel_coeff[2][6] = 0.25; - _vel_coeff[2][7] = 0.25; - - _T_coeff[6] = 0.125; - _T_coeff[7] = 0.5; - } -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - using Constants::pi; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // function = B + A * sin(2*pi*f_x*(x-phi_x)) * - // sin(2*pi*f_y*(y-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_function = - [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - double val = coeff[0] - * sin(2.0 * pi * coeff[2] * (x[0] - coeff[3])) - * sin(2.0 * pi * coeff[4] * (x[1] - coeff[5])); - double return_val = num_space_dim == 2 - ? val + coeff[1] - : val - * sin(2.0 * pi * coeff[6] - * (x[2] - coeff[7])) - + coeff[1]; - return return_val; - }; - - Kokkos::Array x; - for (int dim = 0; dim < num_space_dim; ++dim) - x[dim] = _ip_coords(cell, point, dim); - - _lagrange_pressure(cell, point) = set_function(_phi_coeff, x); - _temperature(cell, point) = set_function(_T_coeff, x); - for (int i = 0; i < num_space_dim; ++i) - _velocity[i](cell, point) = set_function(_vel_coeff[i], x); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_METHODMANUFACTUREDSOLUTION_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_MetricTensor.cpp b/src/closure_models/VertexCFD_Closure_MetricTensor.cpp deleted file mode 100644 index ed16d57..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensor.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MetricTensor.hpp" -#include "VertexCFD_Closure_MetricTensor_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::MetricTensor) diff --git a/src/closure_models/VertexCFD_Closure_MetricTensor.hpp b/src/closure_models/VertexCFD_Closure_MetricTensor.hpp deleted file mode 100644 index bf5c0fe..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensor.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METRICTENSOR_HPP -#define VERTEXCFD_CLOSURE_METRICTENSOR_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class MetricTensor : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField - _metric_tensor; - - MetricTensor(const panzer::IntegrationRule& ir); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - template - KOKKOS_INLINE_FUNCTION void - operator()(std::integral_constant, - const int cell, - const int point) const; - - private: - const int _ir_degree; - const int _num_topo_dim; - int _ir_index; - - PHX::MDField - _jacobian; - - Kokkos::View _element_map; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METRICTENSOR_HPP diff --git a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.cpp b/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.cpp deleted file mode 100644 index 18b53b7..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MetricTensorElementLength.hpp" -#include "VertexCFD_Closure_MetricTensorElementLength_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::MetricTensorElementLength) diff --git a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp b/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp deleted file mode 100644 index baa416e..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_HPP -#define VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class MetricTensorElementLength - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _element_length; - - MetricTensorElementLength(const panzer::IntegrationRule& ir, - const std::string& prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _metric_tensor; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_HPP diff --git a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength_impl.hpp b/src/closure_models/VertexCFD_Closure_MetricTensorElementLength_impl.hpp deleted file mode 100644 index 2625f44..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensorElementLength_impl.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_IMPL_HPP -#define VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_IMPL_HPP - -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MetricTensorElementLength::MetricTensorElementLength( - const panzer::IntegrationRule& ir, const std::string& prefix) - : _element_length(prefix + "element_length", ir.dl_vector) - , _metric_tensor("metric_tensor", ir.dl_tensor) -{ - this->addEvaluatedField(_element_length); - this->addDependentField(_metric_tensor); - this->setName("Metric Tensor Element Length"); -} - -//---------------------------------------------------------------------------// -template -void MetricTensorElementLength::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MetricTensorElementLength::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _metric_tensor.extent(1); - const int num_space_dim = _metric_tensor.extent(2); - - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, num_point), - [&](const int point) { - using std::sqrt; - for (int i = 0; i < num_space_dim; ++i) - { - _element_length(cell, point, i) - = sqrt(_metric_tensor(cell, point, i, i)); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METRICTENSORELEMENTLENGTH_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_MetricTensor_impl.hpp b/src/closure_models/VertexCFD_Closure_MetricTensor_impl.hpp deleted file mode 100644 index f5438bb..0000000 --- a/src/closure_models/VertexCFD_Closure_MetricTensor_impl.hpp +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_METRICTENSOR_IMPL_HPP -#define VERTEXCFD_CLOSURE_METRICTENSOR_IMPL_HPP - -#include - -#include - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MetricTensor::MetricTensor(const panzer::IntegrationRule& ir) - : _metric_tensor("metric_tensor", ir.dl_tensor) - , _ir_degree(ir.cubature_degree) - , _num_topo_dim(ir.topology->getDimension()) - , _element_map(Kokkos::ViewAllocateWithoutInitializing("element_map"), - _num_topo_dim, - _num_topo_dim) -{ - // - // Define the mapping between the ideal and the reference element. - // - auto map = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _element_map); - switch (ir.topology->getBaseKey()) - { - case shards::Line<>::key: - map(0, 0) = 2.0; - - break; - case shards::Triangle<>::key: - map(0, 0) = 1.0; - map(0, 1) = 0.0; - - map(1, 0) = -1.0 / std::sqrt(3.0); - map(1, 1) = 2.0 / std::sqrt(3.0); - - break; - case shards::Quadrilateral<>::key: - map(0, 0) = 2.0; - map(0, 1) = 0.0; - - map(1, 0) = 0.0; - map(1, 1) = 2.0; - - break; - case shards::Tetrahedron<>::key: - map(0, 0) = 1.0; - map(0, 1) = 0.0; - map(0, 2) = 0.0; - - map(1, 0) = -1.0 / std::sqrt(3.0); - map(1, 1) = 2.0 / std::sqrt(3.0); - map(1, 2) = 0.0; - - map(2, 0) = -1.0 / std::sqrt(6.0); - map(2, 1) = -1.0 / std::sqrt(6.0); - map(2, 2) = std::sqrt(3.0 / 2.0); - - break; - case shards::Pyramid<>::key: - map(0, 0) = 2.0; - map(0, 1) = 0.0; - map(0, 2) = 0.0; - - map(1, 0) = 0.0; - map(1, 1) = 2.0; - map(1, 2) = 0.0; - - map(2, 0) = 0.0; - map(2, 1) = 0.0; - map(2, 2) = std::sqrt(2.0); - - break; - case shards::Wedge<>::key: - map(0, 0) = 1.0; - map(0, 1) = 0.0; - map(0, 2) = 0.0; - - map(1, 0) = -1.0 / std::sqrt(3.0); - map(1, 1) = 2.0 / std::sqrt(3.0); - map(1, 2) = 0.0; - - map(2, 0) = 0.0; - map(2, 1) = 0.0; - map(2, 2) = 2.0; - - break; - case shards::Hexahedron<>::key: - map(0, 0) = 2.0; - map(0, 1) = 0.0; - map(0, 2) = 0.0; - - map(1, 0) = 0.0; - map(1, 1) = 2.0; - map(1, 2) = 0.0; - - map(2, 0) = 0.0; - map(2, 1) = 0.0; - map(2, 2) = 2.0; - - break; - default: - using namespace std::string_literals; - throw std::runtime_error("Invalid base cell topology: "s - + ir.topology->getBaseName()); - } - Kokkos::deep_copy(_element_map, map); - - this->addEvaluatedField(_metric_tensor); - this->setName("Metric Tensor"); -} - -//---------------------------------------------------------------------------// -template -void MetricTensor::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MetricTensor::evaluateFields( - typename Traits::EvalData workset) -{ - _jacobian = workset.int_rules[_ir_index]->jac; - - const int num_cell = workset.num_cells; - const int num_point = _jacobian.extent(1); - - if (_num_topo_dim == 1) - { - Kokkos::MDRangePolicy, - std::integral_constant> - policy({0, 0}, {num_cell, num_point}); - - Kokkos::parallel_for(this->getName(), policy, *this); - } - else if (_num_topo_dim == 2) - { - Kokkos::MDRangePolicy, - std::integral_constant> - policy({0, 0}, {num_cell, num_point}); - - Kokkos::parallel_for(this->getName(), policy, *this); - } - else if (_num_topo_dim == 3) - { - Kokkos::MDRangePolicy, - std::integral_constant> - policy({0, 0}, {num_cell, num_point}); - - Kokkos::parallel_for(this->getName(), policy, *this); - } -} - -//---------------------------------------------------------------------------// -template -template -void MetricTensor::operator()( - std::integral_constant, - const int cell, - const int point) const -{ - constexpr int num_space_dim = NumSpaceDim; - - // - // Transform Jacobian to ideal element. - // - double jac[num_space_dim][num_space_dim]{}; - - for (int i = 0; i < num_space_dim; ++i) - for (int j = 0; j < num_space_dim; ++j) - for (int k = 0; k < num_space_dim; ++k) - jac[i][j] += _jacobian(cell, point, i, k) * _element_map(j, k); - - // - // Compute metric tensor (covariant) from ideal Jacobian. - // - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - _metric_tensor(cell, point, i, j) = 0.0; - for (int k = 0; k < num_space_dim; ++k) - _metric_tensor(cell, point, i, j) += jac[i][k] * jac[j][k]; - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_METRICTENSOR_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_SingularValueElementLength.cpp b/src/closure_models/VertexCFD_Closure_SingularValueElementLength.cpp deleted file mode 100644 index 9ae15da..0000000 --- a/src/closure_models/VertexCFD_Closure_SingularValueElementLength.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_SingularValueElementLength.hpp" -#include "VertexCFD_Closure_SingularValueElementLength_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::SingularValueElementLength) diff --git a/src/closure_models/VertexCFD_Closure_SingularValueElementLength.hpp b/src/closure_models/VertexCFD_Closure_SingularValueElementLength.hpp deleted file mode 100644 index a54cba1..0000000 --- a/src/closure_models/VertexCFD_Closure_SingularValueElementLength.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_HPP -#define VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class SingularValueElementLength - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _element_length; - - SingularValueElementLength(const panzer::IntegrationRule& ir, - const std::string& method, - const std::string& prefix = ""); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - int _ir_degree; - int _ir_index; - - enum class Method - { - Min, - Max - }; - Method _method; - - PHX::MDField - _cell_jac; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_HPP diff --git a/src/closure_models/VertexCFD_Closure_SingularValueElementLength_impl.hpp b/src/closure_models/VertexCFD_Closure_SingularValueElementLength_impl.hpp deleted file mode 100644 index fb59bec..0000000 --- a/src/closure_models/VertexCFD_Closure_SingularValueElementLength_impl.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_IMPL_HPP -#define VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_IMPL_HPP - -#include -#include - -#include -#include - -/** -This class implements the logic to calculate the size of a mesh element -from the Jacobian matrix. The implementation follows the same logic as in -MFEM -(https://mfem.github.io/doxygen/html/classmfem_1_1Mesh.html#adde50e5a10f09b877c861d8371981c0c) -**/ - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -SingularValueElementLength::SingularValueElementLength( - const panzer::IntegrationRule& ir, - const std::string& method, - const std::string& prefix) - : _element_length(prefix + "element_length", ir.dl_vector) - , _ir_degree(ir.cubature_degree) -{ - if (method == "singular_value_min") - { - _method = Method::Min; - } - else if (method == "singular_value_max") - { - _method = Method::Max; - } - else - { - const std::string msg = - "Element Length Method '" + method + - "' is not a correct input.\n" - "Choose between 'singular_value_min' or 'singular_value_max'"; - throw std::runtime_error(msg); - } - - this->addEvaluatedField(_element_length); - this->setName("Singular Value Element Length"); -} - -//---------------------------------------------------------------------------// -template -void SingularValueElementLength::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void SingularValueElementLength::evaluateFields( - typename Traits::EvalData workset) -{ - _cell_jac = workset.int_rules[_ir_index]->jac; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void SingularValueElementLength::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _element_length.extent(1); - const int num_space_dim = _element_length.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::sqrt; - using std::fmin; - using std::fmax; - - // Calculate the singular values of the jacobian matrix 'J' for - // cell 'cell' and point 'point'. The singular values are the - // square roots of the eigenvalues of the symmetric matrix 'J \cdot - // J^t' that are computed by finding the roots of the quadratic - // polynomial 'a \lambda^2 + b \lambda + c' - const auto a11 = _cell_jac(cell, point, 0, 0); - const auto a12 = _cell_jac(cell, point, 0, 1); - const auto a21 = _cell_jac(cell, point, 1, 0); - const auto a22 = _cell_jac(cell, point, 1, 1); - - // Set the coefficients 'a', 'b' and 'c' - const double a = 1.0; - const double b = -(a11 * a11 + a12 * a12 + a21 * a21 + a22 * a22); - const double c = -(a11 * a21 + a12 * a22) * (a11 * a21 + a12 * a22) - + (a11 * a11 + a12 * a12) - * (a21 * a21 + a22 * a22); - - // Compute delta value and make it is positive - const double delta = fmax(b * b - 4 * a * c, 0.0); - - // Compute eigenvalues 'lambda1' and 'lambda2' - const double lambda1 = 0.5 * (-b - sqrt(delta)) / a; - const double lambda2 = 0.5 * (-b + sqrt(delta)) / a; - - // Set 'h' based on the value of 'method' - const double h2 = _method == Method::Min ? fmin(lambda1, lambda2) - : fmax(lambda1, lambda2); - const double h = sqrt(h2); - - // Set value of the element length (same value for all - // directions) - for (int i = 0; i < num_space_dim; i++) - { - _element_length(cell, point, i) = h; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_SINGULARVALUEELEMENTLENGTH_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.cpp b/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.cpp deleted file mode 100644 index 6db6511..0000000 --- a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp" -#include "closure_models/VertexCFD_Closure_VectorFieldDivergence_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::VectorFieldDivergence) diff --git a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp b/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp deleted file mode 100644 index cd56a5e..0000000 --- a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_HPP -#define VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Closure to compute divergence of a vector field -//---------------------------------------------------------------------------// -template -class VectorFieldDivergence : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - VectorFieldDivergence(const panzer::IntegrationRule& ir, - const std::string& field_name, - const std::string& closure_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - int _num_grad_dim; - bool _use_abs; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_vector_field; - - public: - PHX::MDField _vector_field_divergence; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_HPP diff --git a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence_impl.hpp b/src/closure_models/VertexCFD_Closure_VectorFieldDivergence_impl.hpp deleted file mode 100644 index 0715fd0..0000000 --- a/src/closure_models/VertexCFD_Closure_VectorFieldDivergence_impl.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_IMPL_HPP - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -VectorFieldDivergence::VectorFieldDivergence( - const panzer::IntegrationRule& ir, - const std::string& field_name, - const std::string& closure_name) - : _num_grad_dim(ir.spatial_dimension) - , _use_abs(closure_name == "AbsVectorFieldDivergence" ? true : false) - , _vector_field_divergence( - (_use_abs ? "abs_divergence_" : "divergence_") + field_name, - ir.dl_scalar) -{ - // Evaluated fields - this->addEvaluatedField(_vector_field_divergence); - - // Dependent fields - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_vector_field, "GRAD_" + field_name + "_"); - - this->setName("Vector Field Divergence " + std::to_string(_num_grad_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void VectorFieldDivergence::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(policy, *this, this->getName()); -} - -//---------------------------------------------------------------------------// -template -void VectorFieldDivergence::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_vector_field[0].extent(1); - const double abs_tol = 1.0e-12; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _vector_field_divergence(cell, point) = 0.0; - for (int d = 0; d < _num_grad_dim; ++d) - { - _vector_field_divergence(cell, point) - += _grad_vector_field[d](cell, point, d); - } - if (_use_abs) - { - _vector_field_divergence(cell, point) = SmoothMath::abs( - _vector_field_divergence(cell, point), abs_tol); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_VECTORFIELDDIVERGENCE_IMPL_HPP diff --git a/src/closure_models/VertexCFD_Closure_WallDistance.cpp b/src/closure_models/VertexCFD_Closure_WallDistance.cpp deleted file mode 100644 index 5721d52..0000000 --- a/src/closure_models/VertexCFD_Closure_WallDistance.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_WallDistance.hpp" -#include "VertexCFD_Closure_WallDistance_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::WallDistance) diff --git a/src/closure_models/VertexCFD_Closure_WallDistance.hpp b/src/closure_models/VertexCFD_Closure_WallDistance.hpp deleted file mode 100644 index 6450b92..0000000 --- a/src/closure_models/VertexCFD_Closure_WallDistance.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_WALLDISTANCE_HPP -#define VERTEXCFD_CLOSURE_WALLDISTANCE_HPP - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include "Panzer_CommonArrayFactories.hpp" -#include -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Wall Distance evaluator. -//---------------------------------------------------------------------------// -template -class WallDistance : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - struct EvaluateTag - { - }; - struct RegistrationTag - { - }; - - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - WallDistance(const panzer::IntegrationRule& ir, - Teuchos::RCP mesh_manager, - const Teuchos::ParameterList closure_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()(EvaluateTag, const int cell) const; - - KOKKOS_INLINE_FUNCTION - void operator()(RegistrationTag, const int cell) const; - - public: - // Panzer field for storing wall distance - PHX::MDField _distance; - - private: - // Integration rule cubature degree needed for getting integration point - // coordinates - double _ir_degree; - - // Integration rule index needed for getting integration point coordinates - double _ir_index; - - // number of worksets - int _number_worksets; - - // current workset, needed for accessing _distance_vector in - // PostRegistration Setup and EvaluateFields - std::size_t _current_workset = 0; - - // Field of the coordinates of the integration points - PHX::MDField _ip_coords; - - Teuchos::RCP _topology; - unsigned _key; - - // View for storing the vector of global side data - Kokkos::View _sides; - - // View for storing the distance field across all worksets - Kokkos::View _distance_vector; - - // View for storing the surface normal of global sides - Kokkos::View _normals; - - // Vector which stores the workset_id for each workset - std::vector _workset_id; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_WALLDISTANCE_HPP diff --git a/src/closure_models/VertexCFD_Closure_WallDistance_impl.hpp b/src/closure_models/VertexCFD_Closure_WallDistance_impl.hpp deleted file mode 100644 index 51f10e8..0000000 --- a/src/closure_models/VertexCFD_Closure_WallDistance_impl.hpp +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_WALLDISTANCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_WALLDISTANCE_IMPL_HPP - -#include -#include -#include - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ - -//---------------------------------------------------------------------------// -template -WallDistance::WallDistance( - const panzer::IntegrationRule& ir, - Teuchos::RCP mesh_manager, - const Teuchos::ParameterList closure_params) - : _distance("distance", ir.dl_scalar) - , _ir_degree(ir.cubature_degree) -{ - this->addEvaluatedField(_distance); - this->setName("distance"); - - // Extract the wall names from input to be used to construct the _sides - // view - std::vector wall_names; - auto wall_names_list = closure_params.get("Wall Names"); - panzer::StringTokenizer(wall_names, wall_names_list, ",", true); - - // create a sidesetGeometry instance and store the side data in the _sides - // view - VertexCFD::Mesh::Topology::SidesetGeometry surfaces(mesh_manager->mesh(), - wall_names); - _topology = surfaces.topology(); - _key = _topology->getKey(); - _sides = Kokkos::create_mirror_view(surfaces.sides()); - - _normals = Kokkos::View( - "normals", _sides.extent(0), num_space_dim); -} - -//---------------------------------------------------------------------------// -template -void WallDistance::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); - _number_worksets = (*sd.worksets_).size(); - - // Initialize the size of the _distance_vector view - _distance_vector - = Kokkos::View("distance_vector", - _number_worksets, - _distance.extent(0), - _distance.extent(1)); - - // size the iterator vectors for storing intial workset cell data - _workset_id.resize(_number_worksets); - - // Loop over worksets to calculate wall distance and fill iterator data - for (std::size_t wks = 0; wks < (*sd.worksets_).size(); ++wks) - { - _current_workset = wks; - panzer::Workset workset = (*sd.worksets_)[wks]; - // If the workset is empty, skip the iterator setup - if (workset.num_cells > 0) - { - _workset_id[wks] = workset.getIdentifier(); - } - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = Kokkos::RangePolicy( - 0, workset.num_cells); - // Call operator for wall distance over cells in workset "wks" - Kokkos::parallel_for(this->getName(), policy, *this); - } -} - -//---------------------------------------------------------------------------// - -//---------------------------------------------------------------------------// -template -void WallDistance::evaluateFields( - typename Traits::EvalData workset) -{ - // Only fill distance info if the workset is not null - if (workset.num_cells > 0) - { - for (int i = 0; i < _number_worksets; ++i) - { - // Check which workset is the current one - if (_workset_id[i] == workset.getIdentifier()) - { - _current_workset = i; - break; - } - } - - // Loop over cells in the workset and points in the cell and set the - // distance based off pre-calculated value - auto policy = Kokkos::RangePolicy( - 0, workset.num_cells); - Kokkos::parallel_for("SetDistanceField", policy, *this); - } -} -//---------------------------------------------------------------------------// - -//---------------------------------------------------------------------------// -template -void WallDistance::operator()(RegistrationTag, - const int cell) const -{ - const int num_point = _distance.extent(1); - for (int point = 0; point < num_point; ++point) - { - double distance = 1e8; - double temp = 1e8; - - // loop over sides and calculate the minimum distance to the current ip - for (long unsigned int n = 0; n < _sides.extent(0); ++n) - { - // call the triangle distance function and store the distance if - // it is a minimum - if constexpr (num_space_dim == 3) - { - if (_key == shards::Tetrahedron<4>::key) - { - int index[3] = {0, 1, 2}; - temp = GeometryPrimitives::distanceToTriangleFace( - _sides, _normals, n, _ip_coords, cell, point, index); - } - if (_key == shards::Hexahedron<8>::key) - { - int index[3] = {0, 1, 2}; - double t2 = GeometryPrimitives::distanceToTriangleFace( - _sides, _normals, n, _ip_coords, cell, point, index); - index[1] = 2; - index[2] = 3; - double t1 = GeometryPrimitives::distanceToTriangleFace( - _sides, _normals, n, _ip_coords, cell, point, index); - temp = std::fmin(t1, t2); - } - } - if constexpr (num_space_dim == 2) - { - int index[2] = {0, 1}; - temp = GeometryPrimitives::distanceToLinearEdge( - _sides, n, _ip_coords, cell, point, 2, index); - } - if (temp < distance) - { - distance = temp; - } - } - - _distance_vector(_current_workset, cell, point) = distance; - } -} -//---------------------------------------------------------------------------// - -//---------------------------------------------------------------------------// -template -void WallDistance::operator()(EvaluateTag, - const int cell) const -{ - const int num_point = _distance.extent(1); - for (int point = 0; point < num_point; ++point) - { - _distance(cell, point) - = _distance_vector(_current_workset, cell, point); - } -} -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_WALLDISTANCE_IMPL_HPP diff --git a/src/closure_models/unit_test/CMakeLists.txt b/src/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index caeaa5d..0000000 --- a/src/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - ClosureModelFactoryTestHarness - ElementLength - ExternalMagneticField - MetricTensor - MetricTensorElementLength - MethodManufacturedSolution - MethodManufacturedSolutionSource - MeasureElementLength - SingularValueElementLength - WallDistance - VectorFieldDivergence - ConstantScalarField - ) - -VertexCFD_add_tests( - MPI - LIBS VertexCFD - NAMES - ExternalFields - ) diff --git a/src/closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp b/src/closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp deleted file mode 100644 index 58f74b7..0000000 --- a/src/closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef VERTEXCFD_CLOSUREMODELFACTORYTESTHARNESS_HPP -#define VERTEXCFD_CLOSUREMODELFACTORYTESTHARNESS_HPP - -#include "closure_models/VertexCFD_ClosureModelFactory.hpp" -#include "drivers/VertexCFD_MeshManager.hpp" - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Closure model factory test fixture. -template -struct ClosureModelFactoryTestFixture -{ - int num_evaluators = 1; - int eval_index = 0; - std::string type_name{"!!! UNDEFINED !!!"}; - std::string eval_name{"!!! UNDEFINED !!!"}; - Teuchos::ParameterList user_params{"User Data"}; - Teuchos::ParameterList model_params{"Model Data"}; - - ClosureModelFactoryTestFixture() - { - // These gas properties are currently always required in the factory. - user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - Parameter::ParameterDatabase parameter_db(comm); - - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "Inline"); - auto& inline_params = mesh_params->sublist("Inline"); - inline_params.set("Element Type", "Tet4"); - auto& mesh_details = inline_params.sublist("Mesh"); - mesh_details.set("X0", 0.0); - mesh_details.set("Xf", 1.0); - mesh_details.set("X Elements", 1); - mesh_details.set("Y0", -1.0); - mesh_details.set("Yf", 1.0); - mesh_details.set("Y Elements", 1); - mesh_details.set("Z0", -1.0); - mesh_details.set("Zf", 1.0); - mesh_details.set("Z Elements", 1); - - Teuchos::RCP mesh_manager{ - Teuchos::rcp(new MeshManager(parameter_db, comm))}; - user_params.set const>("MeshManager", - mesh_manager); - mesh_manager->completeMeshConstruction(); - } - - template - void buildAndTest() - { - constexpr int num_space_dim = NumSpaceDim; - - const std::string model_id{"Test Model"}; - - model_params.set("Type", type_name); - - Teuchos::ParameterList closure_params{"Closure Models"}; - closure_params - .sublist(model_id) // Model ID sublist - .set("Model Data", model_params); // sublist for this closure model - - // Set up a trivial integration_rule for the factory. - const panzer::IntegrationDescriptor integration_desc( - 0, panzer::IntegrationDescriptor::VOLUME); - auto cell_topo = Teuchos::rcp(new shards::CellTopology( - shards::getCellTopologyData>())); - const int num_cells = 0; - auto integration_rule = Teuchos::rcp(new panzer::IntegrationRule( - integration_desc, cell_topo, num_cells)); - - // This is unused but passed by non-const reference, so cannot be - // constructed in the argument list. - PHX::FieldManager fm; - - ClosureModel::Factory factory; - auto evaluators = factory.buildClosureModels( - model_id, - closure_params, - panzer::FieldLayoutLibrary{}, // unused - integration_rule, - Teuchos::ParameterList{}, // unused (default_params) - user_params, - Teuchos::null, // unused (global_data) - fm); - - // Make sure the factory returned something. - ASSERT_FALSE(evaluators.is_null()); - - // The factory should return a single evaluator. - ASSERT_EQ(num_evaluators, evaluators->size()); - auto eval = evaluators->at(eval_index); - - // The evaluator should be castable to the derived evaluator type. - auto evalPtr = dynamic_cast(eval.get()); - EXPECT_NE(nullptr, evalPtr) << "Factory returned unexpected " - "evaluator."; - - // The evaluator should have the expected name. - EXPECT_EQ(eval_name, eval->getName()); - } -}; - -} // namespace Test -} // namespace VertexCFD - -#endif // VERTEXCFD_CLOSUREMODELFACTORYTESTHARNESS_HPP diff --git a/src/closure_models/unit_test/doc/MethodManufacuredSolution2D_reference.py b/src/closure_models/unit_test/doc/MethodManufacuredSolution2D_reference.py deleted file mode 100644 index c9f43ea..0000000 --- a/src/closure_models/unit_test/doc/MethodManufacuredSolution2D_reference.py +++ /dev/null @@ -1,41 +0,0 @@ -from math import * - -# gas properties -gamma = 1.5 -univ_gas_const = 2.0 -mmw = 3.0 -R = univ_gas_const / mmw -one_over_gm1 = 1.0 / (gamma - 1.0) - -# coefficient of sinusoidal mms function -rho_coeff = [0.0125, 0.25, 0.125, 0.5, 0.0, 1.0] -u_coeff = [0.0125, 0.125, 0.125, 0.0, 0.25, 0.08] -v_coeff = [0.0375, 0.25, 0.375, 0.0, 0.5, 1.125] -w_coeff = [0.025, 0.125, 0.25, 1.0, 0.25, 0.0] -T_coeff = [0.0625, 0.375, 0.25, 0.25, 0.5, 1.0] - -# evaluation points -x = 0.5 -y = 0.5 - -# mms solution -# f = A * sin( 2*pi*f_x * ( x - phi_x ) ) * sin( 2*pi*f_y * ( y - phi_y ) ) + B -rho = rho_coeff[0] * sin(2.0 * pi * rho_coeff[1] * (x - rho_coeff[3])) * sin( - 2.0 * pi * rho_coeff[2] * (y - rho_coeff[4])) + rho_coeff[5] - -u = u_coeff[0] * sin(2.0 * pi * u_coeff[1] * - (x - u_coeff[3])) * sin(2.0 * pi * u_coeff[2] * - (y - u_coeff[4])) + u_coeff[5] - -v = v_coeff[0] * sin(2.0 * pi * v_coeff[1] * - (x - v_coeff[3])) * sin(2.0 * pi * v_coeff[2] * - (y - v_coeff[4])) + v_coeff[5] - -T = T_coeff[0] * sin(2.0 * pi * T_coeff[1] * - (x - T_coeff[3])) * sin(2.0 * pi * T_coeff[2] * - (y - T_coeff[4])) + T_coeff[5] - -print("rho: ", rho) -print("u: ", u) -print("v: ", v) -print("T: ", T) diff --git a/src/closure_models/unit_test/doc/MethodManufacuredSolution3D_reference.py b/src/closure_models/unit_test/doc/MethodManufacuredSolution3D_reference.py deleted file mode 100644 index c000063..0000000 --- a/src/closure_models/unit_test/doc/MethodManufacuredSolution3D_reference.py +++ /dev/null @@ -1,54 +0,0 @@ -from math import * - -# gas properties -gamma = 1.5 -univ_gas_const = 2.0 -mmw = 3.0 -R = univ_gas_const / mmw -one_over_gm1 = 1.0 / (gamma - 1.0) - -# coefficient of sinusoidal mms function -rho_coeff = [0.0125, 0.25, 0.125, 0.375, 0.5, 0.0, 1.0, 1.0] -u_coeff = [0.0125, 0.125, 0.125, 0.25, 0.0, 0.25, 0.0, 0.08] -v_coeff = [0.0375, 0.25, 0.375, 0.25, 0.0, 0.5, 0.5, 1.125] -w_coeff = [0.025, 0.125, 0.25, 0.25, 1.0, 0.25, 0.25, 0.0] -T_coeff = [0.0625, 0.375, 0.25, 0.125, 0.25, 0.5, 0.5, 1.0] - -# evaluation points -x = 0.5 -y = 0.5 -z = 0.5 - -# mms solution -# f = A * sin( 2*pi*f_x * ( x - phi_x ) ) * sin( 2*pi*f_y * ( y - phi_y ) ) -# * sin( 2*pi*f_z * ( z - phi_z ) ) + B -rho = rho_coeff[0] * sin(2.0 * pi * rho_coeff[1] * (x - rho_coeff[4])) * sin( - 2.0 * pi * rho_coeff[2] * - (y - rho_coeff[5])) * sin(2.0 * pi * rho_coeff[3] * - (z - rho_coeff[6])) + rho_coeff[7] - -u = u_coeff[0] * sin(2.0 * pi * u_coeff[1] * (x - u_coeff[4])) * sin( - 2.0 * pi * u_coeff[2] * - (y - u_coeff[5])) * sin(2.0 * pi * u_coeff[3] * - (z - u_coeff[6])) + u_coeff[7] - -v = v_coeff[0] * sin(2.0 * pi * v_coeff[1] * (x - v_coeff[4])) * sin( - 2.0 * pi * v_coeff[2] * - (y - v_coeff[5])) * sin(2.0 * pi * v_coeff[3] * - (z - v_coeff[6])) + v_coeff[7] - -w = w_coeff[0] * sin(2.0 * pi * w_coeff[1] * (x - w_coeff[4])) * sin( - 2.0 * pi * w_coeff[2] * - (y - w_coeff[5])) * sin(2.0 * pi * w_coeff[3] * - (z - w_coeff[6])) + w_coeff[7] - -T = T_coeff[0] * sin(2.0 * pi * T_coeff[1] * (x - T_coeff[4])) * sin( - 2.0 * pi * T_coeff[2] * - (y - T_coeff[5])) * sin(2.0 * pi * T_coeff[3] * - (z - T_coeff[6])) + T_coeff[7] - -print("rho: ", rho) -print("u: ", u) -print("v: ", v) -print("w: ", w) -print("T: ", T) diff --git a/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource2D_reference.nb b/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource2D_reference.nb deleted file mode 100644 index f23da02..0000000 --- a/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource2D_reference.nb +++ /dev/null @@ -1,858 +0,0 @@ -(* Content-type: application/vnd.wolfram.mathematica *) - -(*** Wolfram Notebook File ***) -(* http://www.wolfram.com/nb *) - -(* CreatedBy='Mathematica 12.1' *) - -(*CacheID: 234*) -(* Internal cache information: -NotebookFileLineBreakTest -NotebookFileLineBreakTest -NotebookDataPosition[ 158, 7] -NotebookDataLength[ 29430, 850] -NotebookOptionsPosition[ 26845, 811] -NotebookOutlinePosition[ 27241, 827] -CellTagsIndexPosition[ 27198, 824] -WindowFrame->Normal*) - -(* Beginning of Notebook Content *) -Notebook[{ -Cell[BoxData[{ - RowBox[{"Remove", "[", "\"\\"", "]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"(*", " ", - RowBox[{"material", " ", "property"}], "*)"}], "`"}]}], "Input", - CellChangeTimes->{{3.848745445279336*^9, 3.848745464753326*^9}, { - 3.8487475808945*^9, 3.848747630926712*^9}, 3.848747792303294*^9, { - 3.848765311373025*^9, 3.848765314592247*^9}}, - EmphasizeSyntaxErrors->True, - CellLabel-> - "In[165]:=",ExpressionUUID->"4467992d-fdba-4959-a6bf-6d08d2779d83"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"mu", " ", "=", " ", "4.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gamma", " ", "=", " ", "1.5"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"univgas", " ", "=", " ", "2.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"mmw", " ", "=", " ", "3.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"kappa", " ", "=", " ", "5.0"}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"oneovergm1", " ", "=", " ", - RowBox[{"1.0", "/", - RowBox[{"(", - RowBox[{"gamma", "-", "1.0"}], ")"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"R", " ", "=", " ", - RowBox[{"univgas", "/", "mmw"}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"coefficients", " ", "of", " ", "function"}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"rhocoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "6"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"ucoeff", " ", "=", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "6"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"vcoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "6"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "6"}], "]"}]}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.0"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "1.0"}], ";"}], - "\[IndentingNewLine]"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.08"}], ";"}], - "\n"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "1.125"}], ";"}], - "\[IndentingNewLine]"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0625"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "1.0"}], ";"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"non", "-", - RowBox[{"conservative", " ", "variables"}]}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"rho", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "4", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->CompressedData[" -1:eJxTTMoPSmViYGAQAWIQfcs1evXTNW8cjSVWbQfRHlUiR0H0QyfzEyB6yoP9 -r0B0z2PP1yD6w8WWryD6UMVvgWdAek3DYg0QPW2/kDmIrnPNsQbRy2L3uIHo -9gsF3iBaNboBTEdWzQwG0XLdWdEgOslGKAFE7zrwPRGs7wJTFoh+tYApH0QL -PH9VD6Jf//sOpt9qfW0D0SJmEHqZvHc/iF5ivW46iM7K7XJ4DqSbl74A0ykK -Jx+8ANJGLM7PQXQkp/j59yD/qm+/CaLfBYq+BtFMa2I+gug/2x+0fATSl04J -dYFox0/9k78D6VqDhWD67J4PTGe3vnFM60xmBtEA9Q/CBw== - "], - CellLabel-> - "In[166]:=",ExpressionUUID->"dcc5d53d-2af9-432b-b6d1-6486fec9185d"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"vel0", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"ucoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"ucoeff", "[", - RowBox[{"[", "4", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"ucoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"ucoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"ucoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"vel1", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"vcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"vcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"vcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"vcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"vcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"T", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848746226610272*^9, 3.848746278034956*^9}, { - 3.8487508556958323`*^9, 3.848750861646223*^9}, {3.848750906883363*^9, - 3.8487509120131273`*^9}, {3.8487699966809177`*^9, 3.848769997749824*^9}}, - CellLabel-> - "In[202]:=",ExpressionUUID->"4a41c371-f128-4acb-986f-9fa3f019e3b3"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"p", "=", - RowBox[{"rho", "*", "R", "*", "T"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"e", " ", "=", " ", - RowBox[{ - RowBox[{"p", "/", "rho"}], " ", "*", " ", "oneovergm1"}]}], ";"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"convection", " ", "flux"}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcont0", "=", - RowBox[{"rho", "*", "vel0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom00", "=", - RowBox[{ - RowBox[{"rho", "*", "vel0", "*", "vel0"}], "+", "p"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom10", "=", - RowBox[{"rho", "*", "vel0", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"convener0", "=", - RowBox[{ - RowBox[{"(", - RowBox[{ - RowBox[{"rho", "*", - RowBox[{"(", - RowBox[{"e", "+", - RowBox[{"0.5", "*", - RowBox[{"(", - RowBox[{ - RowBox[{"vel0", "*", "vel0"}], "+", - RowBox[{"vel1", "*", "vel1"}]}], ")"}]}]}], ")"}]}], "+", "p"}], - ")"}], "*", "vel0"}]}], ";"}], "\n"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcont1", "=", - RowBox[{"rho", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom01", "=", - RowBox[{"rho", "*", "vel0", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom11", "=", - RowBox[{ - RowBox[{"rho", "*", "vel1", "*", "vel1"}], "+", "p"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convener1", "=", - RowBox[{ - RowBox[{"(", - RowBox[{ - RowBox[{"rho", "*", - RowBox[{"(", - RowBox[{"e", "+", - RowBox[{"0.5", "*", - RowBox[{"(", - RowBox[{ - RowBox[{"vel0", "*", "vel0"}], "+", - RowBox[{"vel1", "*", "vel1"}]}], ")"}]}]}], ")"}]}], "+", "p"}], - ")"}], "*", "vel1"}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848746962514339*^9, 3.848746979291951*^9}, { - 3.848747286562456*^9, 3.848747299308403*^9}}, - CellLabel-> - "In[205]:=",ExpressionUUID->"01f7c5dd-5b69-47be-8753-e24d14b2285b"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"gradu00", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel0", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu01", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel0", ",", "y"}], "]"}]}], ";"}]}]}]], "Input", - CellChangeTimes->{{3.848747831394905*^9, 3.848747876991654*^9}, { - 3.848769199265695*^9, 3.848769202931725*^9}, {3.848769256002901*^9, - 3.848769258425282*^9}}, - CellLabel-> - "In[215]:=",ExpressionUUID->"e60d27cd-143c-4af1-ba1b-f25168f64c0e"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"gradu10", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel1", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu11", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel1", ",", "y"}], "]"}]}], ";"}]}], "Input", - CellChangeTimes->{{3.84874788420791*^9, 3.848747891102584*^9}, { - 3.848769206947481*^9, 3.84876921399562*^9}, {3.848769263241959*^9, - 3.848769265952162*^9}}, - CellLabel-> - "In[217]:=",ExpressionUUID->"9fe74626-97e8-415d-985b-18c87bd949eb"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"trgradu", "=", - RowBox[{ - RowBox[{"1.0", "/", "3.0"}], "*", - RowBox[{"(", - RowBox[{"gradu00", "+", "gradu11"}], ")"}]}]}], ";"}], "\n", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau00", "=", - RowBox[{"2.0", "*", "mu", "*", - RowBox[{"(", - RowBox[{"gradu00", "-", "trgradu"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau01", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu01", "+", "gradu10"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau10", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu10", "+", "gradu01"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau11", "=", - RowBox[{"2.0", "*", "mu", "*", - RowBox[{"(", - RowBox[{"gradu11", "-", "trgradu"}], ")"}]}]}], ";"}]}]}]], "Input", - CellChangeTimes->{{3.8487478960690517`*^9, 3.848747913329102*^9}}, - CellLabel-> - "In[219]:=",ExpressionUUID->"3d4a9929-9fd3-4791-a148-b6d4af575913"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"gradT0", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"T", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradT1", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"T", ",", "y"}], "]"}]}], ";"}]}], "Input", - CellChangeTimes->{ - 3.8487478931723633`*^9, {3.8487479274187183`*^9, 3.848747945619481*^9}, { - 3.848769220349659*^9, 3.848769225427722*^9}, {3.848769268337512*^9, - 3.848769270804284*^9}}, - CellLabel-> - "In[224]:=",ExpressionUUID->"90d01278-1ed2-4278-9896-dd1be1a82d89"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"(*", " ", - RowBox[{"viscous", " ", "flux"}], " ", "*)"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"visccont0", "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom00", "=", "tau00"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom10", "=", "tau01"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscener0", "=", - RowBox[{ - RowBox[{"vel0", "*", "tau00"}], "+", - RowBox[{"vel1", "*", "tau01"}], " ", "-", - RowBox[{"kappa", "*", "gradT0"}]}]}], ";"}], "\[IndentingNewLine]", - "\n", - RowBox[{ - RowBox[{"visccont1", "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom01", "=", "tau10"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom11", "=", "tau11"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscener1", "=", - RowBox[{ - RowBox[{"vel0", "*", "tau10"}], "+", - RowBox[{"vel1", "*", "tau11"}], "-", - RowBox[{"kappa", "*", "gradT1"}]}]}], ";"}]}]}]], "Input", - CellChangeTimes->{{3.848747960288867*^9, 3.848748006260416*^9}, { - 3.848748099191264*^9, 3.8487481026806517`*^9}, {3.853420051521824*^9, - 3.853420055696653*^9}, {3.853420098619451*^9, 3.8534200987593403`*^9}}, - CellLabel-> - "In[226]:=",ExpressionUUID->"e52ab23b-2d72-46e9-9813-f9ead716a4e4"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"total", " ", "flux", " ", "scoure"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"fluxcont0", "=", - RowBox[{"convcont0", "-", "visccont0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom00", "=", - RowBox[{"convmom00", "-", "viscmom00"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom10", "=", - RowBox[{"convmom10", "-", "viscmom10"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxener0", "=", - RowBox[{"convener0", "-", "viscener0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxcont1", "=", - RowBox[{"convcont1", "-", "visccont1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom01", "=", - RowBox[{"convmom01", "-", "viscmom01"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom11", "=", - RowBox[{"convmom11", "-", "viscmom11"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxener1", "=", - RowBox[{"convener1", "-", "viscener1"}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"convective", " ", "flux", " ", "scoure"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvcont0", "=", "convcont0"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom00", "=", "convmom00"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom10", "=", "convmom10"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvener0", "=", "convener0"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvcont1", "=", "convcont1"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom01", "=", "convmom01"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom11", "=", "convmom11"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvener1", "=", "convener1"}], ";"}], - "\[IndentingNewLine]"}]}]], "Input", - CellChangeTimes->{{3.8487481055919733`*^9, 3.848748139478759*^9}, - 3.8534131670114393`*^9, {3.853413260070479*^9, 3.8534132802449408`*^9}, { - 3.85341412952063*^9, 3.8534141793792057`*^9}, {3.8534185439381237`*^9, - 3.853418590788804*^9}, {3.8534216590998907`*^9, 3.8534216660116463`*^9}, { - 3.8535057753777847`*^9, 3.853505778439568*^9}}, - CellLabel-> - "In[234]:=",ExpressionUUID->"937d0cc3-6511-4003-bc5a-cd6cd4274e1f"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{ - "compute", " ", "source", " ", "terms", " ", "by", " ", "this", " ", "MMS", - " ", "plugged", " ", "into", " ", "NS", " ", "eq"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"contsrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxcont0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxcont1", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}]}]], "Input", - CellChangeTimes->{{3.848748142883045*^9, 3.848748248678442*^9}, - 3.853412763643166*^9, {3.8534216004392*^9, 3.853421600567239*^9}}, - CellLabel-> - "In[250]:=",ExpressionUUID->"e4d025b4-8308-44f1-a925-4e3a1e1b7aab"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"mom0src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxmom00", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom01", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8487482383018436`*^9, 3.848748269490571*^9}, { - 3.8534216051933327`*^9, 3.85342160527468*^9}}, - CellLabel-> - "In[251]:=",ExpressionUUID->"5873ca8f-5ac0-44d5-b0dc-c3aa73c9976f"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"mom1src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxmom10", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom11", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.848748282245516*^9, 3.848748288384053*^9}, { - 3.853421609249914*^9, 3.853421615012842*^9}}, - CellLabel-> - "In[252]:=",ExpressionUUID->"c641f2ca-6faa-4856-a473-2399e7b93399"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{ - RowBox[{"enersrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxener0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxener1", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{ - "compute", " ", "convective", " ", "source", " ", "terms", " ", "by", " ", - "this", " ", "MMS", " ", "plugged", " ", "into", " ", "NS", " ", "eq"}], - " ", "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcontsrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvcont0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvcont1", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convmom0src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom00", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom01", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convmom1src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom10", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom11", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convenersrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvener0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvener1", ",", - RowBox[{"{", "y", "}"}]}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848748293923893*^9, 3.8487483403101597`*^9}, { - 3.853412983976652*^9, 3.853413038864058*^9}, 3.853413162608437*^9, { - 3.853416937575151*^9, 3.85341697486716*^9}, {3.853417163333325*^9, - 3.853417294278317*^9}, {3.853418598325819*^9, 3.853418620311182*^9}, { - 3.853421618995813*^9, 3.85342161967478*^9}, {3.853505755600286*^9, - 3.85350576867237*^9}}, - CellLabel-> - "In[253]:=",ExpressionUUID->"c7b79fc9-fdd2-4604-88f5-5cb6b6767542"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"testvals", " ", "=", " ", - RowBox[{"{", - RowBox[{ - RowBox[{"x", "\[Rule]", "0.5"}], ",", " ", - RowBox[{"y", "\[Rule]", "0.5"}]}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.848768648468796*^9, 3.8487686688884172`*^9}, - 3.8535057187739477`*^9}, - CellLabel-> - "In[258]:=",ExpressionUUID->"cbf531c1-0700-43d8-be24-0921976c2046"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{ - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"contsrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"mom0src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"mom1src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"enersrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convcontsrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convmom0src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convmom1src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convenersrc", "/.", "testvals"}], ",", "16"}], - "]"}]}]}]], "Input", - CellChangeTimes->{{3.848752863176736*^9, 3.848752874011384*^9}, { - 3.848752905424728*^9, 3.84875290832469*^9}, {3.8487657731951227`*^9, - 3.848765795363201*^9}, 3.848768642408615*^9, {3.848768675758319*^9, - 3.848768679176118*^9}, {3.853412771710568*^9, 3.853412830306747*^9}, { - 3.853505709321966*^9, 3.853505730153659*^9}, {3.85350580692212*^9, - 3.853505807722196*^9}}, - CellLabel-> - "In[259]:=",ExpressionUUID->"bf442d50-8053-48e4-941e-fed131a4770b"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.06485567288359178\"\>", - 0.06485567288359177, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.853505850711261*^9}, - CellLabel-> - "Out[259]//NumberForm=",ExpressionUUID->"312901ee-07bf-480f-b544-\ -6cbb162c0045"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.1109339876576471\"\>", - -0.11093398765764714`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.8535058507131147`*^9}, - CellLabel-> - "Out[260]//NumberForm=",ExpressionUUID->"4e471ea3-bd14-4aeb-ad55-\ -ddcfbb5f5996"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.1702966657887896\"\>", - 0.17029666578878958`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.8535058507140007`*^9}, - CellLabel-> - "Out[261]//NumberForm=",ExpressionUUID->"7c0217b3-6fa8-4000-96b9-\ -c2d95b266f4e"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.3321719304832673\"\>", - 0.3321719304832673, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.853505850715164*^9}, - CellLabel-> - "Out[262]//NumberForm=",ExpressionUUID->"5e69f283-6d6e-4ec6-9a91-\ -5484792ce295"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.06485567288359178\"\>", - 0.06485567288359177, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.8535058507163677`*^9}, - CellLabel-> - "Out[263]//NumberForm=",ExpressionUUID->"55c392a7-cd70-4146-be2a-\ -751c146294b0"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.01454690044742203\"\>", - 0.014546900447422031`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.853505850717545*^9}, - CellLabel-> - "Out[264]//NumberForm=",ExpressionUUID->"869c1f73-a65f-4e17-9d56-\ -2b8688b5a6eb"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.1796124151976149\"\>", - 0.17961241519761492`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.853505850719009*^9}, - CellLabel-> - "Out[265]//NumberForm=",ExpressionUUID->"eca6f441-c7ce-4939-b42d-\ -37411420ceec"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.3731078016877022\"\>", - 0.3731078016877022, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{3.8535058507202187`*^9}, - CellLabel-> - "Out[266]//NumberForm=",ExpressionUUID->"17d7d742-5131-44f8-8c5a-\ -ac0b8316abfe"] -}, Open ]] -}, -WindowSize->{1398, 747}, -WindowMargins->{{Automatic, -142}, {1, Automatic}}, -FrontEndVersion->"12.1 for Mac OS X x86 (64-bit) (March 18, 2020)", -StyleDefinitions->"Default.nb", -ExpressionUUID->"0bc88581-0d53-4791-969d-68de77380196" -] -(* End of Notebook Content *) - -(* Internal cache information *) -(*CellTagsOutline -CellTagsIndex->{} -*) -(*CellTagsIndex -CellTagsIndex->{} -*) -(*NotebookFileOutline -Notebook[{ -Cell[558, 20, 488, 10, 52, "Input",ExpressionUUID->"4467992d-fdba-4959-a6bf-6d08d2779d83"], -Cell[1049, 32, 5918, 186, 955, "Input",ExpressionUUID->"dcc5d53d-2af9-432b-b6d1-6486fec9185d"], -Cell[6970, 220, 2757, 77, 73, "Input",ExpressionUUID->"4a41c371-f128-4acb-986f-9fa3f019e3b3"], -Cell[9730, 299, 2096, 65, 283, "Input",ExpressionUUID->"01f7c5dd-5b69-47be-8753-e24d14b2285b"], -Cell[11829, 366, 592, 15, 73, "Input",ExpressionUUID->"e60d27cd-143c-4af1-ba1b-f25168f64c0e"], -Cell[12424, 383, 528, 13, 52, "Input",ExpressionUUID->"9fe74626-97e8-415d-985b-18c87bd949eb"], -Cell[12955, 398, 1063, 32, 157, "Input",ExpressionUUID->"3d4a9929-9fd3-4791-a148-b6d4af575913"], -Cell[14021, 432, 553, 14, 52, "Input",ExpressionUUID->"90d01278-1ed2-4278-9896-dd1be1a82d89"], -Cell[14577, 448, 1270, 34, 220, "Input",ExpressionUUID->"e52ab23b-2d72-46e9-9813-f9ead716a4e4"], -Cell[15850, 484, 2239, 57, 451, "Input",ExpressionUUID->"937d0cc3-6511-4003-bc5a-cd6cd4274e1f"], -Cell[18092, 543, 774, 19, 73, "Input",ExpressionUUID->"e4d025b4-8308-44f1-a925-4e3a1e1b7aab"], -Cell[18869, 564, 501, 13, 30, "Input",ExpressionUUID->"5873ca8f-5ac0-44d5-b0dc-c3aa73c9976f"], -Cell[19373, 579, 498, 13, 30, "Input",ExpressionUUID->"c641f2ca-6faa-4856-a473-2399e7b93399"], -Cell[19874, 594, 2293, 60, 157, "Input",ExpressionUUID->"c7b79fc9-fdd2-4604-88f5-5cb6b6767542"], -Cell[22170, 656, 385, 10, 30, "Input",ExpressionUUID->"cbf531c1-0700-43d8-be24-0921976c2046"], -Cell[CellGroupData[{ -Cell[22580, 670, 1665, 42, 241, "Input",ExpressionUUID->"bf442d50-8053-48e4-941e-fed131a4770b"], -Cell[24248, 714, 319, 10, 45, "Output",ExpressionUUID->"312901ee-07bf-480f-b544-6cbb162c0045"], -Cell[24570, 726, 323, 10, 45, "Output",ExpressionUUID->"4e471ea3-bd14-4aeb-ad55-ddcfbb5f5996"], -Cell[24896, 738, 321, 10, 45, "Output",ExpressionUUID->"7c0217b3-6fa8-4000-96b9-c2d95b266f4e"], -Cell[25220, 750, 317, 10, 45, "Output",ExpressionUUID->"5e69f283-6d6e-4ec6-9a91-5484792ce295"], -Cell[25540, 762, 321, 10, 45, "Output",ExpressionUUID->"55c392a7-cd70-4146-be2a-751c146294b0"], -Cell[25864, 774, 321, 10, 45, "Output",ExpressionUUID->"869c1f73-a65f-4e17-9d56-2b8688b5a6eb"], -Cell[26188, 786, 319, 10, 45, "Output",ExpressionUUID->"eca6f441-c7ce-4939-b42d-37411420ceec"], -Cell[26510, 798, 319, 10, 45, "Output",ExpressionUUID->"17d7d742-5131-44f8-8c5a-ac0b8316abfe"] -}, Open ]] -} -] -*) - diff --git a/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource3D_reference.nb b/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource3D_reference.nb deleted file mode 100644 index 4f2efb6..0000000 --- a/src/closure_models/unit_test/doc/MethodManufacuredSolutionSource3D_reference.nb +++ /dev/null @@ -1,1292 +0,0 @@ -(* Content-type: application/vnd.wolfram.mathematica *) - -(*** Wolfram Notebook File ***) -(* http://www.wolfram.com/nb *) - -(* CreatedBy='Mathematica 12.1' *) - -(*CacheID: 234*) -(* Internal cache information: -NotebookFileLineBreakTest -NotebookFileLineBreakTest -NotebookDataPosition[ 158, 7] -NotebookDataLength[ 45124, 1284] -NotebookOptionsPosition[ 42325, 1243] -NotebookOutlinePosition[ 42723, 1259] -CellTagsIndexPosition[ 42680, 1256] -WindowFrame->Normal*) - -(* Beginning of Notebook Content *) -Notebook[{ -Cell[BoxData[{ - RowBox[{"Remove", "[", "\"\\"", "]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"(*", " ", - RowBox[{"material", " ", "property"}], "*)"}], "`"}]}], "Input", - CellChangeTimes->{{3.848745445279336*^9, 3.848745464753326*^9}, { - 3.8487475808945*^9, 3.848747630926712*^9}, 3.848747792303294*^9, { - 3.848765311373025*^9, 3.848765314592247*^9}}, - EmphasizeSyntaxErrors->True, - CellLabel-> - "In[1191]:=",ExpressionUUID->"4467992d-fdba-4959-a6bf-6d08d2779d83"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"mu", " ", "=", " ", "4.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gamma", " ", "=", " ", "1.5"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"univgas", " ", "=", " ", "2.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"mmw", " ", "=", " ", "3.0"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"kappa", " ", "=", " ", "5.0"}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"oneovergm1", " ", "=", " ", - RowBox[{"1.0", "/", - RowBox[{"(", - RowBox[{"gamma", "-", "1.0"}], ")"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"R", " ", "=", " ", - RowBox[{"univgas", "/", "mmw"}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"coefficients", " ", "of", " ", "function"}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"rhocoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "8"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"ucoeff", " ", "=", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "8"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"vcoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "8"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"wcoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "8"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", " ", "=", " ", - RowBox[{"ConstantArray", "[", - RowBox[{"0", ",", "8"}], "]"}]}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.375"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.0"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "7", "]"}], "]"}], "=", "1.0"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "8", "]"}], "]"}], "=", "1.0"}], ";"}], - "\n"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.25"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "7", "]"}], "]"}], "=", "0.0"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "8", "]"}], "]"}], "=", "0.08"}], ";"}], - "\n"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.25"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}], "=", "0.5"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}], "=", "1.125"}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.025"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.125"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.25"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "1.0"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}], "=", "0.25"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}], "=", "0.0"}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "=", "0.0625"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "=", "0.375"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "=", "0.125"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}], "=", "0.25"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}], "=", "0.5"}], ";"}], "\n", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}], "=", "0.5"}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}], "=", "1.0"}], ";"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"non", "-", - RowBox[{"conservative", " ", "variables"}]}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"rho", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"rhocoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"z", "-", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "7", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"rhocoeff", "[", - RowBox[{"[", "8", "]"}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->CompressedData[" -1:eJxTTMoPSmViYGAQB2IQfcs1evXTNW8cjSVWbQfRHlUiR0H0QyfzEyB6yoP9 -r0B0z2PP1yD6w8WWryD6UMVvgWdAek3DYg0QPW2/kDmIrnPNsQbRy2L3uIHo -9gsF3iBaNboBTEdWzQwG0XLdWdEgOslGKAFE7zrwPRGs7wJTFoh+tYApH0QL -PH9VD6Jf//sOpt9qfW0D0SJmEHqZvHc/iF5ivW46iM7K7XJ4DqSbl74A0ykK -Jx+8ANJGLM7PQXQkp/j59yD/qm+/CaLfBYq+BtFMa2I+gug/2x+0fATSl04J -dYFox0/9k78D6VqDhWD67J4PTGe3vnFM60xmBtGzela72dx+46iS/MkTRM8O -f/oIRB+YF/UYRCsE7/kIoiveffoEogHyRt45 - "], - CellLabel-> - "In[1192]:=",ExpressionUUID->"dcc5d53d-2af9-432b-b6d1-6486fec9185d"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"vel0", " ", "=", - RowBox[{ - RowBox[{ - RowBox[{"ucoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"ucoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"ucoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"ucoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"ucoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"ucoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"z", "-", - RowBox[{"ucoeff", "[", - RowBox[{"[", "7", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"ucoeff", "[", - RowBox[{"[", "8", "]"}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"vel1", " ", "=", - RowBox[{ - RowBox[{ - RowBox[{"vcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"vcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"vcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"vcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"vcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"vcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"z", "-", - RowBox[{"vcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"vcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"vel2", " ", "=", - RowBox[{ - RowBox[{ - RowBox[{"wcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"wcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"wcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"wcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"wcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"wcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"z", "-", - RowBox[{"wcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"wcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"T", " ", "=", " ", - RowBox[{ - RowBox[{ - RowBox[{"Tcoeff", "[", - RowBox[{"[", "1", "]"}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "2", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"x", "-", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "5", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "3", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"y", "-", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "6", "]"}], "]"}]}], ")"}]}], "]"}], "*", - RowBox[{"Sin", "[", - RowBox[{"2.0", "*", "Pi", "*", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "4", "]"}], "]"}], "*", - RowBox[{"(", - RowBox[{"z", "-", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "7", "]"}], "]"}]}], ")"}]}], "]"}]}], "+", - RowBox[{"Tcoeff", "[", - RowBox[{"[", "8", "]"}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848746226610272*^9, 3.848746278034956*^9}, { - 3.8487508556958323`*^9, 3.848750861646223*^9}, {3.848750906883363*^9, - 3.8487509120131273`*^9}, {3.8487699966809177`*^9, 3.848769997749824*^9}, - 3.873038934710096*^9, {3.873041903481428*^9, - 3.873041904771916*^9}},ExpressionUUID->"4a41c371-f128-4acb-986f-\ -9fa3f019e3b3"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"p", "=", - RowBox[{"rho", "*", "R", "*", "T"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"e", " ", "=", " ", - RowBox[{ - RowBox[{"p", "/", "rho"}], " ", "*", " ", "oneovergm1"}]}], ";"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"convection", " ", "flux"}], " ", - "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcont0", "=", - RowBox[{"rho", "*", "vel0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom00", "=", - RowBox[{ - RowBox[{"rho", "*", "vel0", "*", "vel0"}], "+", "p"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom01", "=", - RowBox[{"rho", "*", "vel0", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom02", "=", - RowBox[{"rho", "*", "vel0", "*", "vel2"}]}], ";"}], "\[IndentingNewLine]", - - RowBox[{ - RowBox[{ - RowBox[{"convener0", "=", - RowBox[{ - RowBox[{"(", - RowBox[{ - RowBox[{"rho", "*", - RowBox[{"(", - RowBox[{"e", "+", - RowBox[{"0.5", "*", - RowBox[{"(", - RowBox[{ - RowBox[{"vel0", "*", "vel0"}], "+", - RowBox[{"vel1", "*", "vel1"}], "+", - RowBox[{"vel2", "*", "vel2"}]}], ")"}]}]}], ")"}]}], "+", "p"}], - ")"}], "*", "vel0"}]}], ";"}], "\[IndentingNewLine]"}], "\n", - RowBox[{ - RowBox[{"convcont1", "=", - RowBox[{"rho", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom10", "=", - RowBox[{"rho", "*", "vel1", "*", "vel0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom11", "=", - RowBox[{ - RowBox[{"rho", "*", "vel1", "*", "vel1"}], "+", "p"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom12", "=", - RowBox[{"rho", "*", "vel1", "*", "vel2"}]}], ";"}], "\[IndentingNewLine]", - - RowBox[{ - RowBox[{ - RowBox[{"convener1", "=", - RowBox[{ - RowBox[{"(", - RowBox[{ - RowBox[{"rho", "*", - RowBox[{"(", - RowBox[{"e", "+", - RowBox[{"0.5", "*", - RowBox[{"(", - RowBox[{ - RowBox[{"vel0", "*", "vel0"}], "+", - RowBox[{"vel1", "*", "vel1"}], "+", - RowBox[{"vel2", "*", "vel2"}]}], ")"}]}]}], ")"}]}], "+", "p"}], - ")"}], "*", "vel1"}]}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcont2", "=", - RowBox[{"rho", "*", "vel2"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom20", "=", - RowBox[{"rho", "*", "vel2", "*", "vel0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom21", "=", - RowBox[{"rho", "*", "vel2", "*", "vel1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"convmom22", "=", - RowBox[{ - RowBox[{"rho", "*", "vel2", "*", "vel2"}], " ", "+", " ", "p"}]}], - ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convener2", "=", - RowBox[{ - RowBox[{"(", - RowBox[{ - RowBox[{"rho", "*", - RowBox[{"(", - RowBox[{"e", "+", - RowBox[{"0.5", "*", - RowBox[{"(", - RowBox[{ - RowBox[{"vel0", "*", "vel0"}], "+", - RowBox[{"vel1", "*", "vel1"}], "+", - RowBox[{"vel2", "*", "vel2"}]}], ")"}]}]}], ")"}]}], "+", "p"}], - ")"}], "*", "vel2"}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848746962514339*^9, 3.848746979291951*^9}, { - 3.848747286562456*^9, 3.848747299308403*^9}, {3.873038955833394*^9, - 3.873039044655115*^9}, {3.873039107695705*^9, 3.873039109150676*^9}, { - 3.873039139170364*^9, 3.87303921638381*^9}, {3.8730399770625353`*^9, - 3.873039980801319*^9}}, - CellLabel-> - "In[1249]:=",ExpressionUUID->"01f7c5dd-5b69-47be-8753-e24d14b2285b"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"gradu00", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel0", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu01", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel0", ",", "y"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu02", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel0", ",", "z"}], "]"}]}], ";"}]}]}]], "Input", - CellChangeTimes->{{3.848747831394905*^9, 3.848747876991654*^9}, { - 3.848769199265695*^9, 3.848769202931725*^9}, {3.848769256002901*^9, - 3.848769258425282*^9}, {3.873039247850809*^9, 3.8730392518064137`*^9}}, - CellLabel-> - "In[1266]:=",ExpressionUUID->"e60d27cd-143c-4af1-ba1b-f25168f64c0e"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"gradu10", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel1", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu11", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel1", ",", "y"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"gradu12", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel1", ",", "z"}], "]"}]}], ";"}], - "\[IndentingNewLine]"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu20", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel2", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu21", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel2", ",", "y"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradu22", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"vel2", ",", "z"}], "]"}]}], - ";"}], "\[IndentingNewLine]"}], "Input", - CellChangeTimes->{{3.84874788420791*^9, 3.848747891102584*^9}, { - 3.848769206947481*^9, 3.84876921399562*^9}, {3.848769263241959*^9, - 3.848769265952162*^9}, {3.873039257315653*^9, 3.873039275795073*^9}, { - 3.8730399230667343`*^9, 3.873039923736637*^9}, 3.873041612217534*^9}, - CellLabel-> - "In[1269]:=",ExpressionUUID->"9fe74626-97e8-415d-985b-18c87bd949eb"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"trgradu", "=", - RowBox[{ - RowBox[{"1.0", "/", "3.0"}], "*", - RowBox[{"(", - RowBox[{"gradu00", "+", "gradu11", "+", "gradu22"}], ")"}]}]}], ";"}], - "\n", "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau00", "=", - RowBox[{"2.0", "*", "mu", "*", - RowBox[{"(", - RowBox[{"gradu00", "-", "trgradu"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau11", "=", - RowBox[{"2.0", "*", "mu", "*", - RowBox[{"(", - RowBox[{"gradu11", "-", "trgradu"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau22", "=", - RowBox[{"2.0", "*", "mu", "*", - RowBox[{"(", - RowBox[{"gradu22", "-", "trgradu"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau01", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu01", "+", "gradu10"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau02", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu02", "+", "gradu20"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau10", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu10", "+", "gradu01"}], ")"}]}]}], ";"}], "\n", - RowBox[{ - RowBox[{"tau12", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu12", "+", "gradu21"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau20", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu20", "+", "gradu02"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"tau21", "=", - RowBox[{"mu", "*", - RowBox[{"(", - RowBox[{"gradu21", "+", "gradu12"}], ")"}]}]}], ";"}], - "\[IndentingNewLine]"}]}]], "Input", - CellChangeTimes->{{3.8487478960690517`*^9, 3.848747913329102*^9}, { - 3.8730392822142572`*^9, 3.8730393803925533`*^9}}, - CellLabel-> - "In[1275]:=",ExpressionUUID->"3d4a9929-9fd3-4791-a148-b6d4af575913"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"gradT0", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"T", ",", "x"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradT1", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"T", ",", "y"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"gradT2", " ", "=", " ", - RowBox[{"D", "[", - RowBox[{"T", ",", "z"}], "]"}]}], ";"}]}], "Input", - CellChangeTimes->{ - 3.8487478931723633`*^9, {3.8487479274187183`*^9, 3.848747945619481*^9}, { - 3.848769220349659*^9, 3.848769225427722*^9}, {3.848769268337512*^9, - 3.848769270804284*^9}, {3.8730393865659924`*^9, 3.8730393908509207`*^9}}, - CellLabel-> - "In[1285]:=",ExpressionUUID->"90d01278-1ed2-4278-9896-dd1be1a82d89"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"(*", " ", - RowBox[{"viscous", " ", "flux"}], " ", "*)"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"visccont0", "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom00", "=", "tau00"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom01", "=", "tau01"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom02", "=", "tau02"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"viscener0", "=", - RowBox[{ - RowBox[{"vel0", "*", "tau00"}], "+", - RowBox[{"vel1", "*", "tau01"}], "+", - RowBox[{"vel2", "*", "tau02"}], " ", "-", - RowBox[{"kappa", "*", "gradT0"}]}]}], ";"}], "\n", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"visccont1", "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom10", "=", "tau10"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom11", "=", "tau11"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom12", "=", "tau12"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"viscener1", "=", - RowBox[{ - RowBox[{"vel0", "*", "tau10"}], "+", - RowBox[{"vel1", "*", "tau11"}], "+", - RowBox[{"vel2", "*", "tau12"}], "-", - RowBox[{"kappa", "*", "gradT1"}]}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"visccont2", "=", "0.0"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom20", "=", "tau20"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom21", "=", "tau21"}], ";"}], "\n", - RowBox[{ - RowBox[{"viscmom22", "=", "tau22"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"viscener2", "=", - RowBox[{ - RowBox[{"vel0", "*", "tau20"}], "+", - RowBox[{"vel1", "*", "tau21"}], "+", - RowBox[{"vel2", "*", "tau22"}], " ", "-", - RowBox[{"kappa", "*", "gradT2"}]}]}], ";"}]}]}]], "Input", - CellChangeTimes->{{3.848747960288867*^9, 3.848748006260416*^9}, { - 3.848748099191264*^9, 3.8487481026806517`*^9}, {3.853420051521824*^9, - 3.853420055696653*^9}, {3.853420098619451*^9, 3.8534200987593403`*^9}, { - 3.873039404333919*^9, 3.873039513137635*^9}}, - CellLabel-> - "In[1288]:=",ExpressionUUID->"e52ab23b-2d72-46e9-9813-f9ead716a4e4"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"total", " ", "flux", " ", "scoure"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{ - RowBox[{"fluxcont0", "=", - RowBox[{"convcont0", "-", "visccont0"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom00", "=", - RowBox[{"convmom00", "-", "viscmom00"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom10", "=", - RowBox[{"convmom10", "-", "viscmom10"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom20", "=", - RowBox[{"convmom20", "-", "viscmom20"}]}], ";"}], "\[IndentingNewLine]", - - RowBox[{ - RowBox[{"fluxener0", "=", - RowBox[{"convener0", "-", "viscener0"}]}], ";"}], "\n", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxcont1", "=", - RowBox[{"convcont1", "-", "visccont1"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom01", "=", - RowBox[{"convmom01", "-", "viscmom01"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom11", "=", - RowBox[{"convmom11", "-", "viscmom11"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom21", "=", - RowBox[{"convmom21", "-", "viscmom21"}]}], ";"}], "\[IndentingNewLine]", - - RowBox[{ - RowBox[{"fluxener1", "=", - RowBox[{"convener1", "-", "viscener1"}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxcont2", "=", - RowBox[{"convcont2", "-", "visccont2"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom02", "=", - RowBox[{"convmom02", "-", "viscmom02"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom12", "=", - RowBox[{"convmom12", "-", "viscmom12"}]}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxmom22", "=", - RowBox[{"convmom22", "-", "viscmom22"}]}], ";"}], "\[IndentingNewLine]", - - RowBox[{ - RowBox[{"fluxener2", "=", - RowBox[{"convener2", "-", "viscener2"}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{"convective", " ", "flux", " ", "scoure"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvcont0", "=", "convcont0"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom00", "=", "convmom00"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom10", "=", "convmom10"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom20", "=", "convmom20"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvener0", "=", "convener0"}], ";"}], "\n", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvcont1", "=", "convcont1"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom01", "=", "convmom01"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom11", "=", "convmom11"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom21", "=", "convmom21"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvener1", "=", "convener1"}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvcont2", "=", "convcont2"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom02", "=", "convmom02"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom12", "=", "convmom12"}], ";"}], "\n", - RowBox[{ - RowBox[{"fluxconvmom22", "=", "convmom22"}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"fluxconvener2", "=", "convener2"}], ";"}], - "\[IndentingNewLine]"}]}]], "Input", - CellChangeTimes->{{3.8487481055919733`*^9, 3.848748139478759*^9}, - 3.8534131670114393`*^9, {3.853413260070479*^9, 3.8534132802449408`*^9}, { - 3.85341412952063*^9, 3.8534141793792057`*^9}, {3.8534185439381237`*^9, - 3.853418590788804*^9}, {3.8534216590998907`*^9, 3.8534216660116463`*^9}, { - 3.8535057753777847`*^9, 3.853505778439568*^9}, {3.873039553375629*^9, - 3.873039688454216*^9}}, - CellLabel-> - "In[1303]:=",ExpressionUUID->"937d0cc3-6511-4003-bc5a-cd6cd4274e1f"], - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{ - "compute", " ", "source", " ", "terms", " ", "by", " ", "this", " ", "MMS", - " ", "plugged", " ", "into", " ", "NS", " ", "eq"}], " ", "*)"}], - "\[IndentingNewLine]", - RowBox[{ - RowBox[{"contsrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxcont0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxcont1", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxcont2", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}]}]], "Input", - CellChangeTimes->{{3.848748142883045*^9, 3.848748248678442*^9}, - 3.853412763643166*^9, {3.8534216004392*^9, 3.853421600567239*^9}, { - 3.873039696705966*^9, 3.873039717087324*^9}}, - CellLabel-> - "In[1333]:=",ExpressionUUID->"e4d025b4-8308-44f1-a925-4e3a1e1b7aab"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"mom0src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxmom00", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom01", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom02", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8487482383018436`*^9, 3.848748269490571*^9}, { - 3.8534216051933327`*^9, 3.85342160527468*^9}, {3.8730397108342743`*^9, - 3.8730397243434258`*^9}}, - CellLabel-> - "In[1334]:=",ExpressionUUID->"5873ca8f-5ac0-44d5-b0dc-c3aa73c9976f"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{"mom1src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxmom10", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom11", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom12", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"mom2src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxmom20", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom21", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxmom22", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848748282245516*^9, 3.848748288384053*^9}, { - 3.853421609249914*^9, 3.853421615012842*^9}, {3.873039726583364*^9, - 3.873039767094389*^9}}, - CellLabel-> - "In[1335]:=",ExpressionUUID->"c641f2ca-6faa-4856-a473-2399e7b93399"], - -Cell[BoxData[{ - RowBox[{ - RowBox[{ - RowBox[{"enersrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxener0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxener1", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxener2", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - "\[IndentingNewLine]", - RowBox[{"(*", " ", - RowBox[{ - "compute", " ", "convective", " ", "source", " ", "terms", " ", "by", " ", - "this", " ", "MMS", " ", "plugged", " ", "into", " ", "NS", " ", "eq"}], - " ", "*)"}]}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convcontsrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvcont0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvcont1", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvcont2", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convmom0src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom00", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom01", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom02", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convmom1src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom10", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom11", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom12", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convmom2src", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom20", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom21", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvmom22", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"convenersrc", " ", "=", " ", - RowBox[{ - RowBox[{"Grad", "[", - RowBox[{"fluxconvener0", ",", - RowBox[{"{", "x", "}"}]}], "]"}], " ", "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvener1", ",", - RowBox[{"{", "y", "}"}]}], "]"}], "+", - RowBox[{"Grad", "[", - RowBox[{"fluxconvener2", ",", - RowBox[{"{", "z", "}"}]}], "]"}]}]}], ";"}]}], "Input", - CellChangeTimes->{{3.848748293923893*^9, 3.8487483403101597`*^9}, { - 3.853412983976652*^9, 3.853413038864058*^9}, 3.853413162608437*^9, { - 3.853416937575151*^9, 3.85341697486716*^9}, {3.853417163333325*^9, - 3.853417294278317*^9}, {3.853418598325819*^9, 3.853418620311182*^9}, { - 3.853421618995813*^9, 3.85342161967478*^9}, {3.853505755600286*^9, - 3.85350576867237*^9}, {3.873039772050819*^9, 3.8730397766636133`*^9}, { - 3.873039826217679*^9, 3.8730399020648727`*^9}}, - CellLabel-> - "In[1337]:=",ExpressionUUID->"c7b79fc9-fdd2-4604-88f5-5cb6b6767542"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"testvals", " ", "=", " ", - RowBox[{"{", - RowBox[{ - RowBox[{"x", "\[Rule]", "0.5"}], ",", " ", - RowBox[{"y", "\[Rule]", "0.5"}], ",", " ", - RowBox[{"z", "\[Rule]", "0.5"}]}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.848768648468796*^9, 3.8487686688884172`*^9}, - 3.8535057187739477`*^9, {3.8730398601207523`*^9, 3.873039863408111*^9}}, - CellLabel-> - "In[1343]:=",ExpressionUUID->"cbf531c1-0700-43d8-be24-0921976c2046"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{ - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"contsrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"mom0src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"mom1src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"mom2src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"enersrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convcontsrc", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convmom0src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convmom1src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convmom2src", "/.", "testvals"}], ",", "16"}], "]"}], - "\[IndentingNewLine]", - RowBox[{"NumberForm", "[", - RowBox[{ - RowBox[{"convenersrc", "/.", "testvals"}], ",", "16"}], - "]"}]}]}]], "Input", - CellChangeTimes->{{3.848752863176736*^9, 3.848752874011384*^9}, { - 3.848752905424728*^9, 3.84875290832469*^9}, {3.8487657731951227`*^9, - 3.848765795363201*^9}, 3.848768642408615*^9, {3.848768675758319*^9, - 3.848768679176118*^9}, {3.853412771710568*^9, 3.853412830306747*^9}, { - 3.853505709321966*^9, 3.853505730153659*^9}, {3.85350580692212*^9, - 3.853505807722196*^9}, {3.873039872506875*^9, 3.8730398747109547`*^9}, { - 3.8730399067207212`*^9, 3.873039908946208*^9}}, - CellLabel-> - "In[1344]:=",ExpressionUUID->"bf442d50-8053-48e4-941e-fed131a4770b"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.004621895004700585\"\>", - -0.004621895004700585, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617431982*^9}, - CellLabel-> - "Out[1344]//NumberForm=",ExpressionUUID->"510023f0-8695-4149-aa11-\ -13f2306d8668"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.00509066713414899\"\>", - -0.005090667134148991, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617434058*^9}, - CellLabel-> - "Out[1345]//NumberForm=",ExpressionUUID->"027caf61-93aa-413f-9e99-\ -adf812864b32"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.01507826268348906\"\>", - 0.015078262683489059`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617435268*^9}, - CellLabel-> - "Out[1346]//NumberForm=",ExpressionUUID->"e821e49b-ae90-42f3-a805-\ -8a3d26579bc5"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.1749456755521264\"\>", - -0.17494567555212637`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617436906*^9}, - CellLabel-> - "Out[1347]//NumberForm=",ExpressionUUID->"8096696f-0249-4f4c-8e56-\ -c39734d7b1f0"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"0.0104778831052318\"\>", - 0.010477883105231796`, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617438293*^9}, - CellLabel-> - "Out[1348]//NumberForm=",ExpressionUUID->"91b4610b-a721-4f6b-87bd-\ -8ce2919a7871"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.004621895004700585\"\>", - -0.004621895004700585, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617439692*^9}, - CellLabel-> - "Out[1349]//NumberForm=",ExpressionUUID->"d599fede-982d-4506-bc2e-\ -ba45a00d33ae"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.001970095500617289\"\>", - -0.001970095500617289, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617440896*^9}, - CellLabel-> - "Out[1350]//NumberForm=",ExpressionUUID->"68209bbf-a48f-4122-8481-\ -31733e9f9e9d"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.005199631880288158\"\>", - -0.005199631880288158, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.8730416174422703`*^9}, - CellLabel-> - "Out[1351]//NumberForm=",ExpressionUUID->"dd69c10e-7b10-46ef-968f-\ -f17e3f8713ea"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.005749127851644679\"\>", - -0.005749127851644679, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.8730416174434557`*^9}, - CellLabel-> - "Out[1352]//NumberForm=",ExpressionUUID->"10fea2f3-d466-41a4-a2f3-\ -3e985c3966f4"], - -Cell[BoxData[ - TagBox[ - RowBox[{"{", - InterpretationBox["\<\"-0.01193110189893135\"\>", - -0.01193110189893135, - AutoDelete->True], "}"}], - NumberForm[#, 16]& ]], "Output", - CellChangeTimes->{ - 3.853505850711261*^9, {3.873039914424315*^9, 3.873039927911124*^9}, - 3.873039987336241*^9, 3.8730401589823513`*^9, {3.87304026077766*^9, - 3.873040282452457*^9}, 3.873041617444722*^9}, - CellLabel-> - "Out[1353]//NumberForm=",ExpressionUUID->"05870f19-f015-4ac2-995e-\ -6828011ecb5d"] -}, Open ]] -}, -WindowSize->{1830, 1137}, -WindowMargins->{{Automatic, 2150}, {Automatic, 36}}, -FrontEndVersion->"12.1 for Mac OS X x86 (64-bit) (March 18, 2020)", -StyleDefinitions->"Default.nb", -ExpressionUUID->"0bc88581-0d53-4791-969d-68de77380196" -] -(* End of Notebook Content *) - -(* Internal cache information *) -(*CellTagsOutline -CellTagsIndex->{} -*) -(*CellTagsIndex -CellTagsIndex->{} -*) -(*NotebookFileOutline -Notebook[{ -Cell[558, 20, 489, 10, 52, "Input",ExpressionUUID->"4467992d-fdba-4959-a6bf-6d08d2779d83"], -Cell[1050, 32, 8495, 275, 1333, "Input",ExpressionUUID->"dcc5d53d-2af9-432b-b6d1-6486fec9185d"], -Cell[9548, 309, 4772, 134, 94, "Input",ExpressionUUID->"4a41c371-f128-4acb-986f-9fa3f019e3b3"], -Cell[14323, 445, 3595, 109, 451, "Input",ExpressionUUID->"01f7c5dd-5b69-47be-8753-e24d14b2285b"], -Cell[17921, 556, 790, 19, 94, "Input",ExpressionUUID->"e60d27cd-143c-4af1-ba1b-f25168f64c0e"], -Cell[18714, 577, 1283, 33, 178, "Input",ExpressionUUID->"9fe74626-97e8-415d-985b-18c87bd949eb"], -Cell[20000, 612, 2065, 64, 304, "Input",ExpressionUUID->"3d4a9929-9fd3-4791-a148-b6d4af575913"], -Cell[22068, 678, 741, 18, 73, "Input",ExpressionUUID->"90d01278-1ed2-4278-9896-dd1be1a82d89"], -Cell[22812, 698, 2155, 57, 388, "Input",ExpressionUUID->"e52ab23b-2d72-46e9-9813-f9ead716a4e4"], -Cell[24970, 757, 3806, 100, 829, "Input",ExpressionUUID->"937d0cc3-6511-4003-bc5a-cd6cd4274e1f"], -Cell[28779, 859, 932, 23, 73, "Input",ExpressionUUID->"e4d025b4-8308-44f1-a925-4e3a1e1b7aab"], -Cell[29714, 884, 659, 17, 30, "Input",ExpressionUUID->"5873ca8f-5ac0-44d5-b0dc-c3aa73c9976f"], -Cell[30376, 903, 1059, 29, 52, "Input",ExpressionUUID->"c641f2ca-6faa-4856-a473-2399e7b93399"], -Cell[31438, 934, 3354, 88, 178, "Input",ExpressionUUID->"c7b79fc9-fdd2-4604-88f5-5cb6b6767542"], -Cell[34795, 1024, 483, 11, 30, "Input",ExpressionUUID->"cbf531c1-0700-43d8-be24-0921976c2046"], -Cell[CellGroupData[{ -Cell[35303, 1039, 2042, 51, 283, "Input",ExpressionUUID->"bf442d50-8053-48e4-941e-fed131a4770b"], -Cell[37348, 1092, 494, 13, 45, "Output",ExpressionUUID->"510023f0-8695-4149-aa11-13f2306d8668"], -Cell[37845, 1107, 493, 13, 45, "Output",ExpressionUUID->"027caf61-93aa-413f-9e99-adf812864b32"], -Cell[38341, 1122, 492, 13, 45, "Output",ExpressionUUID->"e821e49b-ae90-42f3-a805-8a3d26579bc5"], -Cell[38836, 1137, 492, 13, 45, "Output",ExpressionUUID->"8096696f-0249-4f4c-8e56-c39734d7b1f0"], -Cell[39331, 1152, 491, 13, 45, "Output",ExpressionUUID->"91b4610b-a721-4f6b-87bd-8ce2919a7871"], -Cell[39825, 1167, 494, 13, 45, "Output",ExpressionUUID->"d599fede-982d-4506-bc2e-ba45a00d33ae"], -Cell[40322, 1182, 494, 13, 45, "Output",ExpressionUUID->"68209bbf-a48f-4122-8481-31733e9f9e9d"], -Cell[40819, 1197, 496, 13, 45, "Output",ExpressionUUID->"dd69c10e-7b10-46ef-968f-f17e3f8713ea"], -Cell[41318, 1212, 496, 13, 45, "Output",ExpressionUUID->"10fea2f3-d466-41a4-a2f3-3e985c3966f4"], -Cell[41817, 1227, 492, 13, 45, "Output",ExpressionUUID->"05870f19-f015-4ac2-995e-6828011ecb5d"] -}, Open ]] -} -] -*) - diff --git a/src/closure_models/unit_test/doc/external_magnetic_field.py b/src/closure_models/unit_test/doc/external_magnetic_field.py deleted file mode 100644 index 9a86902..0000000 --- a/src/closure_models/unit_test/doc/external_magnetic_field.py +++ /dev/null @@ -1,10 +0,0 @@ -from math import * - -x = 0.2 -y = 0.3 -r2 = x * x + y * y -B_magn = 1.3 - -print("Bx: ", -y * B_magn / r2) -print("By: ", x * B_magn / r2) -print("Bz: ", 0.0) diff --git a/src/closure_models/unit_test/doc/singularValue_reference.py b/src/closure_models/unit_test/doc/singularValue_reference.py deleted file mode 100644 index c6b9212..0000000 --- a/src/closure_models/unit_test/doc/singularValue_reference.py +++ /dev/null @@ -1,34 +0,0 @@ -# Import Python module to caculcate the singular values -import numpy -from numpy import array -from scipy.linalg import svd -from decimal import * - - -# Function to compute the singular values of the matrix A. -# It prints the eigenvalues and starts with the maximum eigenvalue -def compute_singular_values(A): - U, s, VT = svd(A) - for l in s: - print("Singular values: %.17f" % l) - - -# qp = 0 -print("\033[1mCompute singular values for qp = 0:\033[0m") -J = array([[4.0, 0.0], [0.0, 9.0]]) -compute_singular_values(J) - -# qp = 1 -print("\n\033[1mCompute singular values for qp = 1:\033[0m") -J = array([[2.0, 6.0], [6.0, 2.0]]) -compute_singular_values(J) - -# qp = 2 -print("\n\033[1mCompute singular values for qp = 2:\033[0m") -J = array([[-0.2, 0.3], [0.4, 0.5]]) -compute_singular_values(J) - -# qp = 3 -print("\n\033[1mCompute singular values for qp = 3:\033[0m") -J = array([[2.0, -2.0], [-2.0, 2.0]]) -compute_singular_values(J) diff --git a/src/closure_models/unit_test/tstClampField.cpp b/src/closure_models/unit_test/tstClampField.cpp deleted file mode 100644 index d40d1df..0000000 --- a/src/closure_models/unit_test/tstClampField.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "VertexCFD_ClosureModelFactoryTestHarness.hpp" -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "closure_models/VertexCFD_Closure_ClampField.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test field. -template -struct TestField : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _f; - - PHX::MDField _field; - - TestField(const panzer::IntegrationRule& ir, const double f) - : _f(f) - , _field("test_field", ir.dl_scalar) - { - this->addEvaluatedField(_field); - this->setName("Clamp Field unit test dependency"); - } - - void evaluateFields(typename panzer::Traits::EvalData /*d*/) override - { - _field.deep_copy(_f); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const double f, - const double min, - const double max, - const double tol, - const double exp) -{ - // Setup test fixture. - static constexpr int num_space_dim = 2; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create test field evaluator. - auto dep_field_eval - = Teuchos::rcp(new TestField(*test_fixture.ir, f)); - test_fixture.registerEvaluator(dep_field_eval); - - Teuchos::ParameterList clamp_params{"test_field"}; - clamp_params.set("Lower Limit", min) - .set("Upper Limit", max) - .set("Tolerance", tol); - // Create clamped field evaluator. - auto field_eval - = Teuchos::rcp(new ClosureModel::ClampField( - *test_fixture.ir, clamp_params)); - test_fixture.registerEvaluator(field_eval); - - // Add required test fields. - test_fixture.registerTestField(field_eval->_field); - - // Evaluate. - test_fixture.evaluate(); - - // Check the values. - auto result = test_fixture.getTestFieldData(field_eval->_field); - - EXPECT_DOUBLE_EQ(exp, fieldValue(result, 0, 0)); -} - -//---------------------------------------------------------------------------// -// Minimum clamp test -template -void testMinEval() -{ - const double field_value = -1.0; - testEval(field_value, 0.1, 1.1, 1.0e-16, 0.1); -} - -//---------------------------------------------------------------------------// -// Maximum clamp test -template -void testMaxEval() -{ - const double field_value = 3.5; - testEval(field_value, 0.1, 1.1, 1.0e-16, 1.1); -} - -//---------------------------------------------------------------------------// -// Mid range clamp test -template -void testMidEval() -{ - const double field_value = 0.35; - testEval(field_value, 0.1, 1.1, 1.0e-16, 0.35); -} - -//---------------------------------------------------------------------------// -TEST(ClampFieldMin, residual_test) -{ - testMinEval(); -} - -//---------------------------------------------------------------------------// -TEST(ClampFieldMin, jacobian_test) -{ - testMinEval(); -} - -//---------------------------------------------------------------------------// -TEST(ClampFieldMax, residual_test) -{ - testMaxEval(); -} - -//---------------------------------------------------------------------------// -TEST(ClampFieldMax, jacobian_test) -{ - testMaxEval(); -} -//---------------------------------------------------------------------------// -TEST(ClampFieldMid, residual_test) -{ - testMidEval(); -} - -//---------------------------------------------------------------------------// -TEST(ClampFieldMid, jacobian_test) -{ - testMidEval(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstClosureModelFactoryTestHarness.cpp b/src/closure_models/unit_test/tstClosureModelFactoryTestHarness.cpp deleted file mode 100644 index acba52c..0000000 --- a/src/closure_models/unit_test/tstClosureModelFactoryTestHarness.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -void testDefaultFixture() -{ - ClosureModelFactoryTestFixture test_fixture; - - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - - EXPECT_EQ("!!! UNDEFINED !!!", test_fixture.type_name); - EXPECT_EQ("!!! UNDEFINED !!!", test_fixture.eval_name); - - // The default type_name should not match any closure model and throw an - // exception. - EXPECT_THROW((test_fixture.template buildAndTest()), - std::runtime_error); -} - -TEST(ClosureModelFactoryHarness, residual_test) -{ - testDefaultFixture(); -} - -TEST(ClosureModelFactoryHarness, jacobian_test) -{ - testDefaultFixture(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstConstantScalarField.cpp b/src/closure_models/unit_test/tstConstantScalarField.cpp deleted file mode 100644 index f205631..0000000 --- a/src/closure_models/unit_test/tstConstantScalarField.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include "closure_models/VertexCFD_Closure_ConstantScalarField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - static constexpr int num_space_dim = 3; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - const double value = 1.5; - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::ConstantScalarField( - ir, "scalar_field", value)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_scalar_field); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_scalar_field - = test_fixture.getTestFieldData(eval->_scalar_field); - - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(value, fieldValue(fv_scalar_field, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(ConstantScalarField, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ConstantScalarField, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstElementLength.cpp b/src/closure_models/unit_test/tstElementLength.cpp deleted file mode 100644 index 102321c..0000000 --- a/src/closure_models/unit_test/tstElementLength.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Setup test fixture. - int num_space_dim = 2; - int integration_order = 1; - int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create evaluator. - auto length_eval = Teuchos::rcp( - new ClosureModel::ElementLength( - *test_fixture.ir)); - test_fixture.registerEvaluator(length_eval); - - // Add required test fields. - test_fixture.registerTestField(length_eval->_element_length); - - // Evaluate metric tensor. - test_fixture.evaluate(); - - // Check the metric tensor. - auto element_length_result = test_fixture.getTestFieldData( - length_eval->_element_length); - EXPECT_EQ(1, element_length_result.extent(1)); - EXPECT_DOUBLE_EQ(1.0, fieldValue(element_length_result, 0, 0, 0)); -} - -//---------------------------------------------------------------------------// -TEST(ElementLength, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(ElementLength, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "ElementLength"; - test_fixture.eval_name = "Element Length"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::ElementLength, - num_space_dim>(); -} - -TEST(ElementLength_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(ElementLength_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstExternalFields.cpp b/src/closure_models/unit_test/tstExternalFields.cpp deleted file mode 100644 index 2960bf4..0000000 --- a/src/closure_models/unit_test/tstExternalFields.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include - -#include - -#include - -#include - -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - constexpr auto num_space_dim = std::integral_constant{}; - - // Parse input. Note that are relying on the fact that "simple_box_2d.xml" - // uses a quad mesh and our EvaluatorTestFixture also uses a single quad - // right now. - const std::string location = VERTEXCFD_DRIVER_TEST_INPUT_DIR; - const std::string file = "simple_box_2d.xml"; - std::string file_path = location + file; - - // Create external field data. - auto fields_manager - = Teuchos::rcp(new VertexCFD::ExternalFieldsManager( - num_space_dim, comm, file_path)); - - // Setup test fixture. - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Use the block in the inline mesh generated in "simple_box_2d.xml". - test_fixture.workset->block_id = "eblock-0_0"; - - // If running with multiple ranks, omit the evaluator from one rank. - // All communication has already happened in the ExternalFieldsManager - // constructor above. Previously, the test would hang since the evaluator - // was doing the communication. - if (comm->getRank() == 1) - return; - - // Create an external field evaluator. - const std::string eval_name = "external_fields"; - std::vector field_names - = {"lagrange_pressure", "velocity_0", "velocity_1"}; - auto external_field_eval = Teuchos::rcp( - new ClosureModel::ExternalFields( - eval_name, - fields_manager, - field_names, - test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(external_field_eval); - - // Add required test fields. - test_fixture.registerTestField( - external_field_eval->_external_fields[0]); - test_fixture.registerTestField( - external_field_eval->_external_fields[1]); - test_fixture.registerTestField( - external_field_eval->_external_fields[2]); - - // Evaluate external fields. - test_fixture.evaluate(); - - // Check the fields. - const auto lagrange_pressure = test_fixture.getTestFieldData( - external_field_eval->_external_fields[0]); - const auto velocity_0 = test_fixture.getTestFieldData( - external_field_eval->_external_fields[1]); - const auto velocity_1 = test_fixture.getTestFieldData( - external_field_eval->_external_fields[2]); - const int num_point = test_fixture.cardinality(); - for (int i = 0; i < num_point; ++i) - { - EXPECT_EQ(0.5, fieldValue(lagrange_pressure, 0, i)); - EXPECT_EQ(1.0, fieldValue(velocity_0, 0, i)); - EXPECT_EQ(2.0, fieldValue(velocity_1, 0, i)); - } -} - -//---------------------------------------------------------------------------// -TEST(ExternalFields, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(ExternalFields, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstExternalMagneticField.cpp b/src/closure_models/unit_test/tstExternalMagneticField.cpp deleted file mode 100644 index f0a1f7b..0000000 --- a/src/closure_models/unit_test/tstExternalMagneticField.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include - -#include "closure_models/VertexCFD_Closure_ExternalMagneticField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -enum ExtMagnType -{ - constant, - toroidal -}; - -template -void testEval(const int num_space_dim, - const ExtMagnType ext_magn_type = ExtMagnType::constant) -{ - // Test fixture - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial quadrature points to avoid x = y - if (ext_magn_type == ExtMagnType::toroidal) - { - test_fixture.int_values->ip_coordinates(0, 0, 0) = 0.2; - test_fixture.int_values->ip_coordinates(0, 0, 1) = 0.3; - if (num_space_dim == 3) - test_fixture.int_values->ip_coordinates(0, 0, 2) = 1.0; - } - - const auto& ir = *test_fixture.ir; - - // Initialize class object to test and expected values - Teuchos::ParameterList user_params; - double exp_bx = 0.0; - double exp_by = 0.0; - double exp_bz = 0.0; - if (ext_magn_type == ExtMagnType::constant) - { - user_params.set("External Magnetic Field Type", "constant"); - Teuchos::Array ext_magn_vct(3); - exp_bx = 1.3; - exp_by = 3.5; - exp_bz = 2.4; - ext_magn_vct[0] = exp_bx; - ext_magn_vct[1] = exp_by; - ext_magn_vct[2] = exp_bz; - user_params.set("External Magnetic Field Value", ext_magn_vct); - } - else if (ext_magn_type == ExtMagnType::toroidal) - { - user_params.set("External Magnetic Field Type", "toroidal"); - exp_bx = -3.0; - exp_by = 2.0; - user_params.set("Toroidal Field Magnitude", 1.3); - } - - const auto eval = Teuchos::rcp( - new ClosureModel::ExternalMagneticField( - ir, user_params)); - - // Register - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < 3; ++dim) - test_fixture.registerTestField(eval->_ext_magn_field[dim]); - - test_fixture.evaluate(); - - const auto ext_magn_field_0 - = test_fixture.getTestFieldData(eval->_ext_magn_field[0]); - const auto ext_magn_field_1 - = test_fixture.getTestFieldData(eval->_ext_magn_field[1]); - const auto ext_magn_field_2 - = test_fixture.getTestFieldData(eval->_ext_magn_field[2]); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_bx, fieldValue(ext_magn_field_0, 0, qp)); - EXPECT_EQ(exp_by, fieldValue(ext_magn_field_1, 0, qp)); - EXPECT_EQ(exp_bz, fieldValue(ext_magn_field_2, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(ConstantExternalMagneticField2D, Residual) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(ConstantExternalMagneticField2D, Jacobian) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(OneOverRadiusExternalMagneticField2D, Residual) -{ - testEval(2, ExtMagnType::toroidal); -} - -//-----------------------------------------------------------------// -TEST(OneOverRadiusExternalMagneticField2D, Jacobian) -{ - testEval(2, ExtMagnType::toroidal); -} - -//-----------------------------------------------------------------// -TEST(ExternalMagneticField3D, Residual) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -TEST(ExternalMagneticField3D, Jacobian) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - Teuchos::Array ext_magn_vct(3); - Teuchos::ParameterList user_params; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("External Magnetic Field Value", ext_magn_vct); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.0); - test_fixture.type_name = "ExternalMagneticField"; - test_fixture.eval_name = "External Magnetic Field"; - test_fixture.template buildAndTest< - ClosureModel::ExternalMagneticField, - num_space_dim>(); -} - -TEST(ExternalMagneticField_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(ExternalMagneticField_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstMeasureElementLength.cpp b/src/closure_models/unit_test/tstMeasureElementLength.cpp deleted file mode 100644 index 59d6446..0000000 --- a/src/closure_models/unit_test/tstMeasureElementLength.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include - -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Setup test fixture. - int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create evaluator. - auto measure_element_length_eval = Teuchos::rcp( - new ClosureModel::MeasureElementLength( - *test_fixture.ir)); - test_fixture.registerEvaluator(measure_element_length_eval); - - // Add required test fields. - test_fixture.registerTestField( - measure_element_length_eval->_element_length); - - // Evaluate MFEM element length. - test_fixture.evaluate(); - - // Check the MFEM element length. - auto element_length_result = test_fixture.getTestFieldData( - measure_element_length_eval->_element_length); - int num_point = element_length_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.5, fieldValue(element_length_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ(0.5, fieldValue(element_length_result, 0, qp, 1)); - } -} - -//---------------------------------------------------------------------------// -TEST(MeasureElementLength, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(MeasureElementLength, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MeasureElementLength"; - test_fixture.eval_name = "Measure Element Length"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::MeasureElementLength, - num_space_dim>(); -} - -TEST(MeasureElementLength_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MeasureElementLength_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstMethodManufacturedSolution.cpp b/src/closure_models/unit_test/tstMethodManufacturedSolution.cpp deleted file mode 100644 index 5c14af2..0000000 --- a/src/closure_models/unit_test/tstMethodManufacturedSolution.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(const Kokkos::Array expected_sol) -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create scaled MMS evaluator. - auto mms_eval = Teuchos::rcp( - new ClosureModel:: - MethodManufacturedSolution( - *test_fixture.ir)); - test_fixture.registerEvaluator(mms_eval); - - // Add required test fields. - test_fixture.registerTestField(mms_eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(mms_eval->_velocity[dim]); - test_fixture.registerTestField(mms_eval->_temperature); - - // Evaluate scaled MMS - test_fixture.evaluate(); - - // Check the values - const auto lagrange_pressure_result - = test_fixture.getTestFieldData(mms_eval->_lagrange_pressure); - const auto velocity_0_result - = test_fixture.getTestFieldData(mms_eval->_velocity[0]); - const auto velocity_1_result - = test_fixture.getTestFieldData(mms_eval->_velocity[1]); - const auto temperature_result - = test_fixture.getTestFieldData(mms_eval->_temperature); - - // Check the scaled MMS solutions - int num_point = lagrange_pressure_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(expected_sol[0], - fieldValue(lagrange_pressure_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[1], fieldValue(velocity_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[2], fieldValue(velocity_1_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[num_space_dim + 1], - fieldValue(temperature_result, 0, qp)); - if (num_space_dim == 3) - { - const auto velocity_2_result - = test_fixture.getTestFieldData( - mms_eval->_velocity[2]); - EXPECT_DOUBLE_EQ(expected_sol[3], - fieldValue(velocity_2_result, 0, qp)); - } - } -} - -//---------------------------------------------------------------------------// -template -void testMMS() -{ - constexpr int num_conserve = NumSpaceDim + 2; - // rho vel0 vel1 T - Kokkos::Array expected_value - = {1.0, 0.080933222925629281, 1.125, 1.0}; - // rho vel0 vel1 vel2 T - if (NumSpaceDim == 3) - { - expected_value[0] = 1.0; - expected_value[1] = 0.08065988825907122; - expected_value[2] = 1.125; - expected_value[3] = -0.0014010672786498911; - expected_value[4] = 1.0; - } - testEval(expected_value); -} - -//---------------------------------------------------------------------------// -TEST(MMS2D, residual_test) -{ - testMMS(); -} - -//---------------------------------------------------------------------------// -TEST(MMS2D, jacobian_test) -{ - testMMS(); -} - -//---------------------------------------------------------------------------// -TEST(MMS3D, residual_test) -{ - testMMS(); -} - -//---------------------------------------------------------------------------// -TEST(MMS3D, jacobian_test) -{ - testMMS(); -} -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - const int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MethodManufacturedSolution"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - if (num_space_dim == 2) - test_fixture.eval_name = "Method of Manufactured Solution 2D"; - else if (num_space_dim == 3) - test_fixture.eval_name = "Method of Manufactured Solution 3D"; - test_fixture.template buildAndTest< - ClosureModel::MethodManufacturedSolution, - num_space_dim>(); -} - -TEST(MMS_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MMS_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(MMS_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(MMS_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstMethodManufacturedSolutionSource.cpp b/src/closure_models/unit_test/tstMethodManufacturedSolutionSource.cpp deleted file mode 100644 index 864cb84..0000000 --- a/src/closure_models/unit_test/tstMethodManufacturedSolutionSource.cpp +++ /dev/null @@ -1,244 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(const bool build_viscous_flux, - const Kokkos::Array expected_sol) -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - constexpr int num_conserve = NumSpaceDim + 2; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 4.0); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", true); - fluid_prop_list.set("Thermal conductivity", 5.0); - fluid_prop_list.set("Specific heat capacity", 0.6); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create mms source evaluator. - auto mms_eval = Teuchos::rcp( - new ClosureModel::MethodManufacturedSolutionSource( - *test_fixture.ir, build_viscous_flux, fluid_prop)); - test_fixture.registerEvaluator(mms_eval); - - // Add required test fields. - test_fixture.registerTestField(mms_eval->_continuity_mms_source); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField( - mms_eval->_momentum_mms_source[dim]); - test_fixture.registerTestField(mms_eval->_energy_mms_source); - - // Evaluate mms source - test_fixture.evaluate(); - - // Check the values - const auto continuity_result = test_fixture.getTestFieldData( - mms_eval->_continuity_mms_source); - const auto momentum_0_result = test_fixture.getTestFieldData( - mms_eval->_momentum_mms_source[0]); - const auto momentum_1_result = test_fixture.getTestFieldData( - mms_eval->_momentum_mms_source[1]); - const auto energy_result = test_fixture.getTestFieldData( - mms_eval->_energy_mms_source); - - // Check the mms source solutions - int num_point = continuity_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(expected_sol[0], fieldValue(continuity_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[1], fieldValue(momentum_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[2], fieldValue(momentum_1_result, 0, qp)); - EXPECT_DOUBLE_EQ(expected_sol[num_conserve - 1], - fieldValue(energy_result, 0, qp)); - } - - if (num_space_dim == 3) - { - const auto momentum_2_result = test_fixture.getTestFieldData( - mms_eval->_momentum_mms_source[2]); - for (int qp = 0; qp < num_point; ++qp) - EXPECT_DOUBLE_EQ(expected_sol[num_conserve - 2], - fieldValue(momentum_2_result, 0, qp)); - } -} - -//---------------------------------------------------------------------------// -template -void testMMSSourceConvective() -{ - constexpr int num_space_dim = NumSpaceDim; - - SCOPED_TRACE("Convective"); - Kokkos::Array expected_value; - if (num_space_dim == 2) - { - expected_value[0] = 0.06485567288359177; - expected_value[1] = 0.014546900447422031; - expected_value[2] = 0.1796124151976149; - expected_value[3] = 0.3731078016877022; - } - else if (num_space_dim == 3) - { - expected_value[0] = -0.004621895004700585; - expected_value[1] = -0.001970095500617289; - expected_value[2] = -0.005199631880288158; - expected_value[3] = -0.005749127851644679; - expected_value[4] = -0.01193110189893135; - } - testEval(false, expected_value); -} - -//---------------------------------------------------------------------------// -template -void testMMSSourceConvectiveViscous() -{ - constexpr int num_space_dim = NumSpaceDim; - - SCOPED_TRACE("Convective + Viscous"); - Kokkos::Array expected_value; - if (num_space_dim == 2) - { - expected_value[0] = 0.06485567288359177; - expected_value[1] = -0.11093398765764714; - expected_value[2] = 0.17029666578878958; - expected_value[3] = 0.3321719304832673; - } - else if (num_space_dim == 3) - { - expected_value[0] = -0.004621895004700585; - expected_value[1] = -0.00509066713414899; - expected_value[2] = 0.01507826268348906; - expected_value[3] = -0.17494567555212637; - expected_value[4] = 0.010477883105231796; - } - testEval(true, expected_value); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvective2D, DISABLED_residual_test) -{ - testMMSSourceConvective(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvective2D, DISABLED_jacobian_test) -{ - testMMSSourceConvective(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvectiveViscous2D, DISABLED_residual_test) -{ - testMMSSourceConvectiveViscous(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvectiveViscous2D, DISABLED_jacobian_test) -{ - testMMSSourceConvectiveViscous(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvective3D, DISABLED_residual_test) -{ - testMMSSourceConvective(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvective3D, DISABLED_jacobian_test) -{ - testMMSSourceConvective(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvectiveViscous3D, DISABLED_residual_test) -{ - testMMSSourceConvectiveViscous(); -} - -//---------------------------------------------------------------------------// -TEST(MMSConvectiveViscous3D, DISABLED_jacobian_test) -{ - testMMSSourceConvectiveViscous(); -} - -//---------------------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MethodManufacturedSolutionSource"; - if (num_space_dim == 2) - test_fixture.eval_name = "Method of Manufactured Solution Source 2D"; - else if (num_space_dim == 3) - test_fixture.eval_name = "Method of Manufactured Solution Source 3D"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::MethodManufacturedSolutionSource, - num_space_dim>(); -} - -TEST(MMS_Source_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MMS_Source_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(MMS_Source_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(MMS_Source_Factory3D, jacobian_test) -{ - testFactory(); -} -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstMetricTensor.cpp b/src/closure_models/unit_test/tstMetricTensor.cpp deleted file mode 100644 index b51e780..0000000 --- a/src/closure_models/unit_test/tstMetricTensor.cpp +++ /dev/null @@ -1,588 +0,0 @@ -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(const double (&coords)[CellTopo::node_count][CellTopo::dimension], - const double (&exp)[CellTopo::dimension][CellTopo::dimension]) -{ - constexpr int num_cell = 1; - constexpr int nodes_per_cell = CellTopo::node_count; - constexpr int num_point = 1; - constexpr int num_space_dim = CellTopo::dimension; - - EvaluatorTestFixture::host_coords_view coords_view( - "coords", num_cell, nodes_per_cell, num_space_dim); - for (int i = 0; i < nodes_per_cell; ++i) - for (int j = 0; j < num_space_dim; ++j) - coords_view(0, i, j) = coords[i][j]; - - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture(shards::getCellTopologyData(), - coords_view, - integration_order, - basis_order); - - EXPECT_EQ(nodes_per_cell, test_fixture.cardinality()); - EXPECT_EQ(num_point, test_fixture.numPoint()); - if (::testing::Test::HasFailure()) - { - GTEST_FAIL() << "Unexpected test fixture parameters"; - } - - // Create evaluator. - auto metric_tensor_eval = Teuchos::rcp( - new ClosureModel::MetricTensor( - *test_fixture.ir)); - test_fixture.registerEvaluator(metric_tensor_eval); - - // Add required test fields. - test_fixture.registerTestField( - metric_tensor_eval->_metric_tensor); - - // Evaluate metric tensor. - test_fixture.evaluate(); - - // Check the metric tensor. - auto metric_tensor_result = test_fixture.getTestFieldData( - metric_tensor_eval->_metric_tensor); - - EXPECT_EQ(num_cell, metric_tensor_result.extent(0)); - EXPECT_EQ(num_point, metric_tensor_result.extent(1)); - EXPECT_EQ(num_space_dim, metric_tensor_result.extent(2)); - if (::testing::Test::HasFailure()) - { - GTEST_FAIL() << "Unexpected metric tensor result extents"; - } - - constexpr double tol = 1.0e-14; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - // For zero exptected values, use absolute tolerance. - if (exp[i][j] < tol) - { - EXPECT_NEAR(exp[i][j], metric_tensor_result(0, 0, i, j), tol) - << "M[" << i << ", " << j << ']'; - } - // For non-zero expected values, use relative (ULP) tolerance. - else - { - EXPECT_DOUBLE_EQ(exp[i][j], metric_tensor_result(0, 0, i, j)) - << "M[" << i << ", " << j << ']'; - } - } - } -} - -//---------------------------------------------------------------------------// -// Apply a coordinate transformation given by a rotation/scaling matrix and -// displacement vector. -template -void applyTransformation(double (&coords)[N][D], - const double (&mtx)[D][D], - const double (&disp)[D]) -{ - for (int node = 0; node < N; ++node) - { - double new_coord[D]{}; - for (int i = 0; i < D; ++i) - for (int j = 0; j < D; ++j) - new_coord[i] += mtx[i][j] * coords[node][j]; - for (int i = 0; i < D; ++i) - coords[node][i] = new_coord[i] + disp[i]; - } -} - -//---------------------------------------------------------------------------// -// Test the ideal cell - all edges are length 1. -// The result should be the identity. -template -struct IdealCellTest -{ - static constexpr int N = CellTopo::node_count; - static constexpr int D = CellTopo::dimension; - - static void eval(const double (&coords)[N][D]) - { - double exp[D][D]{}; - for (int i = 0; i < D; ++i) - exp[i][i] = 1.0; - - testEval(coords, exp); - } -}; - -//---------------------------------------------------------------------------// -// Test an isotropic cell - scale, rotate, and displace the ideal cell. -// The result should be scaled but not impacted by rotation or displacement. -template -struct IsotropicCellTest -{ - static constexpr int N = CellTopo::node_count; - static constexpr int D = CellTopo::dimension; - - // Transform 1-D coordinates. - static void transformCoords(double (&coords)[N][1]) - { - // Scale by 5. - constexpr double mtx[1][1] = { - {5.0}, - }; - - // Displace. - constexpr double disp[1] = {1.0}; - - applyTransformation(coords, mtx, disp); - } - - // Transform 2-D coordinates. - static void transformCoords(double (&coords)[N][2]) - { - // Scale by 5 and rotate. - constexpr double mtx[2][2] = { - {4.0, -3.0}, - {3.0, 4.0}, - }; - - // Displace in all directions. - constexpr double disp[2] = {1.0, -2.0}; - - applyTransformation(coords, mtx, disp); - } - - // Transform 3-D coordinates. - static void transformCoords(double (&coords)[N][3]) - { - // Scale by 5 and rotate about three axes. - constexpr double mtx[3][3] = { - {0.12, -3.84, 3.2}, - {3.84, 2.12, 2.4}, - {-3.2, 2.4, 3.0}, - }; - - // Displace in all directions. - constexpr double disp[3] = {1.0, -2.0, 0.5}; - - applyTransformation(coords, mtx, disp); - } - - static void eval(double (&coords)[N][D]) - { - transformCoords(coords); - - double exp[D][D]{}; - for (int i = 0; i < D; ++i) - exp[i][i] = 25.0; - - testEval(coords, exp); - } -}; - -//---------------------------------------------------------------------------// -// Test an anisotropic cell - scale, rotate, and displace the ideal cell. -// The result should be impacted by scaling and rotation, but not displacement. -template -struct AnisotropicCellTest -{ - static constexpr int N = CellTopo::node_count; - static constexpr int D = CellTopo::dimension; - - // Transform 2-D coordinates. - static void transformCoords(double (&coords)[N][2]) - { - // Scale (anisotropically) and rotate. - constexpr double mtx[2][2] = { - {4.0, -1.5}, - {3.0, 2.0}, - }; - - // Displace in all directions. - constexpr double disp[2] = {1.0, -2.0}; - - applyTransformation(coords, mtx, disp); - } - - // Expected 2-D metric. - static void setExpected(double (&exp)[2][2]) - { - exp[0][0] = 18.25; - exp[0][1] = 9.0; - - exp[1][0] = 9.0; - exp[1][1] = 13.0; - } - - // Transform 3-D coordinates. - static void transformCoords(double (&coords)[N][3]) - { - // Scale (anisotropically) and rotate about three axes. - constexpr double mtx[3][3] = { - {0.12, -1.92, 6.4}, - {3.84, 1.06, 4.8}, - {-3.2, 1.2, 6.0}, - }; - - // Displace in all directions. - constexpr double disp[3] = {1.0, -2.0, 0.5}; - - applyTransformation(coords, mtx, disp); - } - - // Expected 3-D metric. - static void setExpected(double (&exp)[3][3]) - { - exp[0][0] = 44.6608; - exp[0][1] = 29.1456; - exp[0][2] = 35.712; - - exp[1][0] = 29.1456; - exp[1][1] = 38.9092; - exp[1][2] = 17.784; - - exp[2][0] = 35.712; - exp[2][1] = 17.784; - exp[2][2] = 47.68; - } - - static void eval(double (&coords)[N][D]) - { - transformCoords(coords); - - double exp[D][D]{}; - setExpected(exp); - - testEval(coords, exp); - } -}; - -//---------------------------------------------------------------------------// -// Test residual and Jacobian evaluations. -template -class MetricTensorTest : public ::testing::Test -{ -}; -using EvalTypes - = ::testing::Types; -class EvalTypeNames -{ - public: - template - static std::string GetName(const int i) - { - if (std::is_same()) - return "Residual"; - if (std::is_same()) - return "Jacobian"; - return std::to_string(i); - } -}; -TYPED_TEST_SUITE(MetricTensorTest, EvalTypes, EvalTypeNames); - -//---------------------------------------------------------------------------// -// Test a 1-D line. -template class CellTest> -void testLine() -{ - // Ideal line. - double coords[2][1] = { - {0.0}, - {1.0}, - }; - - CellTest>::eval(coords); -} - -TYPED_TEST(MetricTensorTest, IdealLine) -{ - testLine(); -} - -TYPED_TEST(MetricTensorTest, IsotropicLine) -{ - testLine(); -} - -//---------------------------------------------------------------------------// -// Test a 2-D triangle. -template class CellTest> -void testTriangle() -{ - // Ideal triangle. - double coords[3][2] = { - {0.0, 0.0}, - {1.0, 0.0}, - {0.5, 0.5 * std::sqrt(3.0)}, - }; - - CellTest>::eval(coords); -} - -TYPED_TEST(MetricTensorTest, IdealTriangle) -{ - testTriangle(); -} - -TYPED_TEST(MetricTensorTest, IsotropicTriangle) -{ - testTriangle(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicTriangle) -{ - testTriangle(); -} - -//---------------------------------------------------------------------------// -// Test a 2-D quadrilateral. -template class CellTest> -void testQuadrilateral() -{ - // Ideal quadrilateral. - double coords[4][2] = { - {0.0, 0.0}, - {1.0, 0.0}, - {1.0, 1.0}, - {0.0, 1.0}, - }; - - CellTest>::eval(coords); -} - -TYPED_TEST(MetricTensorTest, IdealQuadrilateral) -{ - testQuadrilateral(); -} - -TYPED_TEST(MetricTensorTest, IsotropicQuadrilateral) -{ - testQuadrilateral(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicQuadrilateral) -{ - testQuadrilateral(); -} - -//---------------------------------------------------------------------------// -// Test a 3-D tetrahedron. -template class CellTest> -void testTetrahedron() -{ - // Ideal tetrahedron. - double coords[4][3] = { - {0.0, 0.0, 0.0}, - {1.0, 0.0, 0.0}, - {0.5, 0.5 * std::sqrt(3.0), 0.0}, - {0.5, 0.5 / std::sqrt(3.0), std::sqrt(2.0 / 3.0)}, - }; - - CellTest>::eval(coords); -} - -TYPED_TEST(MetricTensorTest, IdealTetrahedron) -{ - testTetrahedron(); -} - -TYPED_TEST(MetricTensorTest, IsotropicTetrahedron) -{ - testTetrahedron(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicTetrahedron) -{ - testTetrahedron(); -} - -//---------------------------------------------------------------------------// -// Test a 3-D hexahedron. -template class CellTest> -void testHexahedron() -{ - // Ideal hexahedron. - double coords[8][3] = { - {0.0, 0.0, 0.0}, - {1.0, 0.0, 0.0}, - {1.0, 1.0, 0.0}, - {0.0, 1.0, 0.0}, - {0.0, 0.0, 1.0}, - {1.0, 0.0, 1.0}, - {1.0, 1.0, 1.0}, - {0.0, 1.0, 1.0}, - }; - - CellTest>::eval(coords); -} - -TYPED_TEST(MetricTensorTest, IdealHexahedron) -{ - testHexahedron(); -} - -TYPED_TEST(MetricTensorTest, IsotropicHexahedron) -{ - testHexahedron(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicHexahedron) -{ - testHexahedron(); -} - -//---------------------------------------------------------------------------// -// Test a 3-D pyramid. -template class CellTest> -void testPyramid() -{ - // Ideal pyramid. - double coords[5][3] = { - {0.0, 0.0, 0.0}, - {1.0, 0.0, 0.0}, - {1.0, 1.0, 0.0}, - {0.0, 1.0, 0.0}, - {0.5, 0.5, 1.0 / std::sqrt(2.0)}, - }; - - // Pyramid cells are not currently supported by panzer. - ASSERT_THROW((CellTest>::eval(coords)), - std::runtime_error); -} - -TYPED_TEST(MetricTensorTest, IdealPyramid) -{ - testPyramid(); -} - -TYPED_TEST(MetricTensorTest, IsotropicPyramid) -{ - testPyramid(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicPyramid) -{ - testPyramid(); -} - -//---------------------------------------------------------------------------// -// Test a 3-D wedge. -template class CellTest> -void testWedge() -{ - // Ideal wedge. - double coords[6][3] = { - {0.0, 0.0, 0.0}, - {1.0, 0.0, 0.0}, - {0.5, 0.5 * std::sqrt(3.0), 0.0}, - {0.0, 0.0, 1.0}, - {1.0, 0.0, 1.0}, - {0.5, 0.5 * std::sqrt(3.0), 1.0}, - }; - - // Wedge cells are not currently supported by panzer. - ASSERT_THROW((CellTest>::eval(coords)), - std::runtime_error); -} - -TYPED_TEST(MetricTensorTest, IdealWedge) -{ - testWedge(); -} - -TYPED_TEST(MetricTensorTest, IsotropicWedge) -{ - testWedge(); -} - -TYPED_TEST(MetricTensorTest, AnisotropicWedge) -{ - testWedge(); -} - -//---------------------------------------------------------------------------// -// Try to construct the evaluator for an unsupported cell topology. -template -void testUnsupportedTopology() -{ - // Set up an integration_rule for an unsupported cell topology. - // ShellQuadrilateral is the only topology supported by Intrepid but not - // by this closure model. - const panzer::IntegrationDescriptor integration_desc( - 0, panzer::IntegrationDescriptor::VOLUME); - auto cell_topo = Teuchos::rcp(new shards::CellTopology( - shards::getCellTopologyData>())); - const int num_cells = 0; - auto integration_rule = Teuchos::rcp( - new panzer::IntegrationRule(integration_desc, cell_topo, num_cells)); - - using MetricTensorEval - = ClosureModel::MetricTensor; - - const std::string msg = "Invalid base cell topology: ShellQuadrilateral_4"; - ASSERT_THROW( - try { - auto metric_tensor_eval = MetricTensorEval(*integration_rule); - } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -TYPED_TEST(MetricTensorTest, UnsupportedTopology) -{ - testUnsupportedTopology(); -} - -//---------------------------------------------------------------------------// -// Test construction from the closure model factory. -template -void testFactory() -{ - constexpr int num_space_dim = 2; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MetricTensor"; - test_fixture.eval_name = "Metric Tensor"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::MetricTensor, - num_space_dim>(); -} - -TYPED_TEST(MetricTensorTest, Factory) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstMetricTensorElementLength.cpp b/src/closure_models/unit_test/tstMetricTensorElementLength.cpp deleted file mode 100644 index 299d5d7..0000000 --- a/src/closure_models/unit_test/tstMetricTensorElementLength.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - static constexpr double _unused - = std::numeric_limits::signaling_NaN(); - - PHX::MDField - _metric_tensor; - - Dependencies(const panzer::IntegrationRule& ir) - : _metric_tensor("metric_tensor", ir.dl_tensor) - { - this->addEvaluatedField(_metric_tensor); - this->setName("Metric Tensor Element Length Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "metric tensor element length test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _metric_tensor.extent(1); - const int num_space_dim = _metric_tensor.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim_i = 0; dim_i < num_space_dim; ++dim_i) - for (int dim_j = 0; dim_j < num_space_dim; ++dim_j) - _metric_tensor(c, qp, dim_i, dim_j) = _unused; - - for (int d = 0; d < num_space_dim; ++d) - { - _metric_tensor(c, qp, d, d) = 0.0625 * (d + 1) * (d + 1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Setup test fixture. - const int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create dependencies. - auto dep_eval = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create evaluator. - auto metric_tensor_eval = Teuchos::rcp( - new ClosureModel::MetricTensorElementLength( - *test_fixture.ir)); - test_fixture.registerEvaluator(metric_tensor_eval); - - // Add required test fields. - test_fixture.registerTestField( - metric_tensor_eval->_element_length); - - // Evaluate metric tensor. - test_fixture.evaluate(); - - // Check the metric tensor. - auto element_length_result = test_fixture.getTestFieldData( - metric_tensor_eval->_element_length); - int num_point = element_length_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.25, fieldValue(element_length_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ(0.50, fieldValue(element_length_result, 0, qp, 1)); - } -} - -//---------------------------------------------------------------------------// -TEST(MetricTensorElementLength, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(MetricTensorElementLength, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MetricTensorElementLength"; - test_fixture.eval_name = "Metric Tensor Element Length"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::MetricTensorElementLength, - num_space_dim>(); -} - -TEST(MetricTensorElementLength_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MetricTensorElementLength_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstSingularValueElementLength.cpp b/src/closure_models/unit_test/tstSingularValueElementLength.cpp deleted file mode 100644 index c4eb2c7..0000000 --- a/src/closure_models/unit_test/tstSingularValueElementLength.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include - -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(const std::string method) -{ - // Setup test fixture. - int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Overwrite Jacobian values with trivial values for each quadrature points - // test_fixture.int_values->jac(cell, qp, i, j) - auto jac_view = test_fixture.int_values->jac.get_static_view(); - auto jac_mirror = Kokkos::create_mirror(jac_view); - - // qp = 0 - jac_mirror(0, 0, 0, 0) = 4.0; - jac_mirror(0, 0, 0, 1) = 0.0; - jac_mirror(0, 0, 1, 0) = 0.0; - jac_mirror(0, 0, 1, 1) = 9.0; - - // qp = 1 - jac_mirror(0, 1, 0, 0) = 2.0; - jac_mirror(0, 1, 0, 1) = 6.0; - jac_mirror(0, 1, 1, 0) = 6.0; - jac_mirror(0, 1, 1, 1) = 2.0; - - // qp = 2 - jac_mirror(0, 2, 0, 0) = -0.2; - jac_mirror(0, 2, 0, 1) = 0.3; - jac_mirror(0, 2, 1, 0) = 0.4; - jac_mirror(0, 2, 1, 1) = 0.5; - - // qp = 3 - jac_mirror(0, 3, 0, 0) = 2.0; - jac_mirror(0, 3, 0, 1) = -2.0; - jac_mirror(0, 3, 1, 0) = -2.0; - jac_mirror(0, 3, 1, 1) = 2.0; - - Kokkos::deep_copy(jac_view, jac_mirror); - - // Create evaluator. - auto singular_value_element_length_eval = Teuchos::rcp( - new ClosureModel::SingularValueElementLength( - *test_fixture.ir, method)); - test_fixture.registerEvaluator( - singular_value_element_length_eval); - - // Add required test fields. - test_fixture.registerTestField( - singular_value_element_length_eval->_element_length); - - // Evaluate MFEM element length. - test_fixture.evaluate(); - - // Check the MFEM element length. - auto element_length_result = test_fixture.getTestFieldData( - singular_value_element_length_eval->_element_length); - int num_point = element_length_result.extent(1); - // Reference values 'sigma' - Kokkos::Array sigma; - sigma[0] = method == "singular_value_max" ? 9.0 : 4.0; - sigma[1] = method == "singular_value_max" ? 8.0 : 4.0; - sigma[2] = method == "singular_value_max" ? 0.65308862983900229 - : 0.33686086382216435; - sigma[3] = method == "singular_value_max" ? 4.0 : 0.0; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(sigma[qp], - fieldValue(element_length_result, 0, qp, 0)); - EXPECT_DOUBLE_EQ(sigma[qp], - fieldValue(element_length_result, 0, qp, 1)); - } -} - -template -void testCatchException(const std::string method) -{ - const std::string msg - = "Element Length Method 'None' is not a correct input.\n" - "Choose between 'singular_value_min' or 'singular_value_max'"; - - EXPECT_THROW( - try { testEval(method); } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthMax, residual_test) -{ - testEval("singular_value_max"); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthMin, residual_test) -{ - testEval("singular_value_min"); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthNone, residual_test) -{ - testCatchException("None"); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthMax, jacobian_test) -{ - testEval("singular_value_max"); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthMin, jacobian_test) -{ - testEval("singular_value_min"); -} - -//---------------------------------------------------------------------------// -TEST(SingularValueElementLengthNone, jacobian_test) -{ - testCatchException("None"); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "SingularValueElementLength"; - test_fixture.eval_name = "Singular Value Element Length"; - test_fixture.model_params.set("Element Length Method", - "singular_value_min"); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::SingularValueElementLength, - num_space_dim>(); -} - -TEST(SingularValueElementLength_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(SingularValueElementLength_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/closure_models/unit_test/tstVectorFieldDivergence.cpp b/src/closure_models/unit_test/tstVectorFieldDivergence.cpp deleted file mode 100644 index 98d0c5e..0000000 --- a/src/closure_models/unit_test/tstVectorFieldDivergence.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include -#include - -#include - -#include "closure_models/VertexCFD_Closure_VectorFieldDivergence.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - double _nanval = std::numeric_limits::quiet_NaN(); - - Kokkos::Array< - PHX::MDField, - 3> - grad_test_field; - - Dependencies(const panzer::IntegrationRule& ir, - const std::string field_name) - { - Utils::addEvaluatedVectorField( - *this, ir.dl_vector, grad_test_field, "GRAD_" + field_name + "_"); - - this->setName("Vector Field Divergence Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "vector field divergence test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_test_field[0].extent(1); - const int num_grad_dim = grad_test_field[0].extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - const double qp1 = qp + 1.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - const double dqp1 = qp1 * (dim + 1.0); - grad_test_field[0](c, qp, dim) = 0.375 * dqp1; - grad_test_field[1](c, qp, dim) = 0.225 * dqp1; - grad_test_field[2](c, qp, dim) - = num_grad_dim == 2 ? _nanval : 0.125 * dqp1; - } - } - } -}; - -template -void testEval(const int num_grad_dim, - const std::string& field_name, - const bool use_abs) -{ - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - auto deps = Teuchos::rcp(new Dependencies(ir, field_name)); - test_fixture.registerEvaluator(deps); - - const std::string closure_name = use_abs ? "AbsVectorFieldDivergence" - : "VectorFieldDivergence"; - const auto eval = Teuchos::rcp( - new ClosureModel::VectorFieldDivergence( - ir, field_name, closure_name)); - test_fixture.registerEvaluator(eval); - - test_fixture.registerTestField(eval->_vector_field_divergence); - - test_fixture.evaluate(); - - const auto div_field = test_fixture.getTestFieldData( - eval->_vector_field_divergence); - - const auto grad_test_field_0 - = test_fixture.getTestFieldData(deps->grad_test_field[0]); - const auto grad_test_field_1 - = test_fixture.getTestFieldData(deps->grad_test_field[1]); - const auto grad_test_field_2 - = test_fixture.getTestFieldData(deps->grad_test_field[2]); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - auto exp_div = fieldValue(grad_test_field_0, 0, qp, 0) - + fieldValue(grad_test_field_1, 0, qp, 1); - if (num_grad_dim > 2) - exp_div += fieldValue(grad_test_field_2, 0, qp, 2); - if (use_abs) - exp_div = std::abs(exp_div); - EXPECT_DOUBLE_EQ(exp_div, fieldValue(div_field, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(Vector2DDivergence2D, residual_test) -{ - testEval(2, "foo", false); -} - -TEST(Vector2DDivergence2D, jacobian_test) -{ - testEval(2, "foo", false); -} - -//-----------------------------------------------------------------// -TEST(Vector3DDivergence2D, residual_test) -{ - testEval(2, "foo", false); -} - -TEST(Vector3DDivergence2D, jacobian_test) -{ - testEval(2, "foo", false); -} -//-----------------------------------------------------------------// -TEST(Vector3DDivergence3D, residual_test) -{ - testEval(3, "bar", false); -} - -TEST(Vector3DDivergence3D, jacobian_test) -{ - testEval(3, "bar", false); -} - -//-----------------------------------------------------------------// -TEST(Vector3DAbsDivergence3D, residual_test) -{ - testEval(3, "bar", true); -} - -TEST(Vector3DAbsDivergence3D, jacobian_test) -{ - testEval(3, "bar", true); -} -//-----------------------------------------------------------------// -template -void testFactory(const std::string& abs_pre = "") -{ - constexpr int num_grad_dim = NumGradDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = abs_pre + "VectorFieldDivergence"; - test_fixture.eval_name = "Vector Field Divergence 2D"; - test_fixture.model_params.set("Field Names", "foo"); - test_fixture.template buildAndTest< - ClosureModel::VectorFieldDivergence, - num_grad_dim>(); -} - -TEST(VectorFieldDivergence_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(VectorFieldDivergence_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(VectorFieldDivergence_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(VectorFieldDivergence_Factory3D, jacobian_test) -{ - testFactory(); -} - -TEST(VectorFieldAbsDivergence_Factory3D, residual_test) -{ - testFactory("Abs"); -} - -TEST(VectorFieldAbsDivergence_Factory3D, jacobian_test) -{ - testFactory("Abs"); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/closure_models/unit_test/tstWallDistance.cpp b/src/closure_models/unit_test/tstWallDistance.cpp deleted file mode 100644 index 6304d0f..0000000 --- a/src/closure_models/unit_test/tstWallDistance.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - int _num_space_dim; - - PHX::MDField _exp_distance; - PHX::MDField _ip_coords; - - int _ir_degree; - - Dependencies(const panzer::IntegrationRule& ir) - : _num_space_dim(ir.spatial_dimension) - , _exp_distance("exp_distance", ir.dl_scalar) - , _ir_degree(ir.cubature_degree) - { - this->addEvaluatedField(_exp_distance); - - this->setName("Wall Distance Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - auto _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, d); - _ip_coords = d.int_rules[_ir_index]->ip_coordinates; - Kokkos::parallel_for( - "Wall Distance Unit Test Dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _exp_distance.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - double y_dist = 1 - std::abs(_ip_coords(c, qp, 1)); - double z_dist = 1e8; - if (_num_space_dim == 3) - { - z_dist = 1 - std::abs(_ip_coords(c, qp, 2)); - } - _exp_distance(c, qp) = std::fmin(y_dist, z_dist); - } - } -}; - -template -void testEval(const std::string element_type) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Construct Mesh parameters and manager - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Make an empty parameter database and build mesh parameters. - Parameter::ParameterDatabase parameter_db(comm); - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "Inline"); - auto& inline_params = mesh_params->sublist("Inline"); - inline_params.set("Element Type", element_type); - auto& mesh_details = inline_params.sublist("Mesh"); - const int nelem_x = 2; - const int nelem_y = 2; - const int nelem_z = 2; - mesh_details.set("X0", 0.0); - mesh_details.set("Xf", 1.0); - mesh_details.set("X Elements", nelem_x); - mesh_details.set("Y0", -1.0); - mesh_details.set("Yf", 1.0); - mesh_details.set("Y Elements", nelem_y); - if (num_space_dim == 3) - { - mesh_details.set("Z0", -1.0); - mesh_details.set("Zf", 1.0); - mesh_details.set("Z Elements", nelem_z); - } - - Teuchos::RCP mesh_manager{ - Teuchos::rcp(new MeshManager(parameter_db, comm))}; - mesh_manager->completeMeshConstruction(); - - Teuchos::ParameterList closure_params; - closure_params.set("Wall Names", "top,bottom,front,back"); - - const auto dep_eval = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(dep_eval); - test_fixture.registerTestField(dep_eval->_exp_distance); - - // Initialize and register closure model - auto eval = Teuchos::rcp( - new ClosureModel::WallDistance( - ir, mesh_manager, closure_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_distance); - - test_fixture.evaluate(); - - // Evaluate and assert closure model values - auto fv_dist = test_fixture.getTestFieldData(eval->_distance); - auto fv_exp_dist - = test_fixture.getTestFieldData(dep_eval->_exp_distance); - - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - // Wall distance - EXPECT_EQ(fieldValue(fv_exp_dist, 0, qp), fieldValue(fv_dist, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(WallDistanceTet4, residual_test) -{ - testEval("Tet4"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceTet4, jacobian_test) -{ - testEval("Tet4"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceTri3, residual_test) -{ - testEval("Tri3"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceTri3, jacobian_test) -{ - testEval("Tri3"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceHex8, residual_test) -{ - testEval("Hex8"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceHex8, jacobian_test) -{ - testEval("Hex8"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceQuad4, residual_test) -{ - testEval("Quad4"); -} - -//-----------------------------------------------------------------// -TEST(WallDistanceQuad4, jacobian_test) -{ - testEval("Quad4"); -} - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "WallDistance"; - test_fixture.eval_name = "distance"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.model_params.set("Wall Names", "dummy"); - test_fixture.template buildAndTest< - ClosureModel::WallDistance, - num_space_dim>(); -} - -TEST(WallDistance_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(WallDistance_Factory3D, jacobian_test) -{ - testFactory(); -} - -TEST(WallDistance_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(WallDistance_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/drivers/VertexCFD_ExternalFieldsManager.hpp b/src/drivers/VertexCFD_ExternalFieldsManager.hpp deleted file mode 100644 index b7ce44e..0000000 --- a/src/drivers/VertexCFD_ExternalFieldsManager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_EXTERNALFIELDSMANAGER_HPP -#define VERTEXCFD_EXTERNALFIELDSMANAGER_HPP - -#include - -#include - -#include - -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -template -class ExternalFieldsManager -{ - public: - template - ExternalFieldsManager( - const std::integral_constant& num_space_dim, - const Teuchos::RCP>& comm, - const std::string& filename); - - Teuchos::RCP globalIndexer() const; - Kokkos::View ghostedFieldData() const; - - private: - // External global indexer (DOF manager). - Teuchos::RCP _global_indexer; - - // Gathered local field values. - Kokkos::View _ghosted_field_data; -}; - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#include "VertexCFD_ExternalFieldsManager_impl.hpp" - -#endif // end VERTEXCFD_EXTERNALFIELDSMANAGER_HPP diff --git a/src/drivers/VertexCFD_ExternalFieldsManager_impl.hpp b/src/drivers/VertexCFD_ExternalFieldsManager_impl.hpp deleted file mode 100644 index aec6cef..0000000 --- a/src/drivers/VertexCFD_ExternalFieldsManager_impl.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef VERTEXCFD_EXTERNALFIELDSMANAGER_IMPL_HPP -#define VERTEXCFD_EXTERNALFIELDSMANAGER_IMPL_HPP - -#include "VertexCFD_InitialConditionManager.hpp" -#include "VertexCFD_MeshManager.hpp" -#include "VertexCFD_PhysicsManager.hpp" -#include "parameters/VertexCFD_ParameterDatabase.hpp" - -#include - -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -template -template -ExternalFieldsManager::ExternalFieldsManager( - const std::integral_constant& num_space_dim, - const Teuchos::RCP>& comm, - const std::string& filename) -{ - auto parameter_db - = Teuchos::rcp(new Parameter::ParameterDatabase(comm, filename)); - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - auto physics_manager = Teuchos::rcp( - new PhysicsManager(num_space_dim, parameter_db, mesh_manager)); - physics_manager->setupModel(); - - _global_indexer = physics_manager->dofManager(); - - auto ic_manager = Teuchos::rcp( - new InitialConditionManager(parameter_db, mesh_manager)); - Teuchos::RCP> solution; - Teuchos::RCP> solution_dot; - ic_manager->applyInitialConditions( - num_space_dim, *physics_manager, solution, solution_dot); - - // Create a global evaluation container for the field data. - Teuchos::RCP ged; - ged = physics_manager->linearObjectFactory()->buildReadOnlyDomainContainer(); - ged->setOwnedVector(solution); - - // Gather the ghosted vector of field data. - ged->globalToGhost(0); - - // Get the local vector data. - auto ghosted_vector - = Teuchos::rcp_dynamic_cast>( - ged->getGhostedVector()); - auto ghosted_data_host = ghosted_vector->getLocalSubVector(); - - // Thyra only provides the ghosted data via a host-side array. - // We need to copy this data to a device view so that it can be accessed - // from the device in kernel below. - _ghosted_field_data = Kokkos::View( - "ghosted_field_data", ghosted_data_host.subDim()); - auto ghost_mirror = Kokkos::create_mirror(_ghosted_field_data); - for (int i = 0; i < ghosted_data_host.subDim(); ++i) - ghost_mirror(i) = ghosted_data_host[i]; - Kokkos::deep_copy(_ghosted_field_data, ghost_mirror); -} - -//---------------------------------------------------------------------------// -template -Teuchos::RCP -ExternalFieldsManager::globalIndexer() const -{ - return _global_indexer; -} - -//---------------------------------------------------------------------------// -template -Kokkos::View -ExternalFieldsManager::ghostedFieldData() const -{ - return _ghosted_field_data; -} - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EXTERNALFIELDSMANAGER_IMPL_HPP diff --git a/src/drivers/VertexCFD_InitialConditionManager.cpp b/src/drivers/VertexCFD_InitialConditionManager.cpp deleted file mode 100644 index f9f66f2..0000000 --- a/src/drivers/VertexCFD_InitialConditionManager.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "VertexCFD_InitialConditionManager.hpp" - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -InitialConditionManager::InitialConditionManager( - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager) - : _parameter_db(parameter_db) - , _mesh_manager(mesh_manager) - , _do_restart(false) - , _t_init(0.0) -{ - // Create restart reader if needed and get the starting time. - auto read_restart_params = _parameter_db->readRestartParameters(); - if (Teuchos::nonnull(read_restart_params)) - { - if (read_restart_params->isType("Read Restart")) - { - _do_restart = read_restart_params->get("Read Restart"); - } - if (_do_restart) - { - auto comm = mesh_manager->comm(); - _restart_reader = Teuchos::rcp(new VertexCFD::Mesh::RestartReader( - comm, *read_restart_params)); - _t_init = _restart_reader->initialStateTime(); - } - } -} - -//---------------------------------------------------------------------------// -double InitialConditionManager::initialTime() const -{ - return _t_init; -} - -//---------------------------------------------------------------------------// -template -void InitialConditionManager::applyInitialConditions( - const std::integral_constant&, - const PhysicsManager& physics_manager, - Teuchos::RCP>& x, - Teuchos::RCP>& x_dot) const -{ - // Initialize 'num_space_dim' with template value - constexpr int num_space_dim = NumSpaceDim; - - // Create vectors if needed. - if (Teuchos::is_null(x)) - { - x = Thyra::createMember( - physics_manager.modelEvaluator()->get_x_space()); - } - if (Teuchos::is_null(x_dot)) - { - x_dot = Thyra::createMember( - physics_manager.modelEvaluator()->get_x_space()); - } - - // Set initial conditions from restart. - if (_do_restart) - { - auto mesh = _mesh_manager->mesh(); - auto dof_manager = physics_manager.dofManager(); - _restart_reader->readSolution(mesh, dof_manager, x, x_dot); - } - - // Set initial conditions from input. - else - { - auto workset_container = physics_manager.worksetContainer(); - auto linear_object_factory = physics_manager.linearObjectFactory(); - auto physics_blocks = physics_manager.physicsBlocks(); - auto user_params = _parameter_db->userParameters(); - auto ic_params = _parameter_db->initialConditionParameters(); - bool write_graph = user_params->get("Output Graph"); - VertexCFD::InitialCondition::FactoryTemplateBuilder - ic_builder(_mesh_manager->mesh()); - panzer::ClosureModelFactory_TemplateManager ic_factory; - ic_factory.buildObjects(ic_builder); - std::map>> - phx_ic_field_managers; - panzer::setupInitialConditionFieldManagers(*workset_container, - physics_blocks, - ic_factory, - *ic_params, - *linear_object_factory, - *user_params, - write_graph, - "", - phx_ic_field_managers); - Teuchos::RCP linear_object_container - = linear_object_factory->buildLinearObjContainer(); - Teuchos::RCP> thyra_loc - = Teuchos::rcp_dynamic_cast>( - linear_object_container); - thyra_loc->set_x_th(x); - panzer::evaluateInitialCondition(*workset_container, - phx_ic_field_managers, - linear_object_container, - *linear_object_factory, - _t_init); - Thyra::assign(x_dot.ptr(), 0.0); - } -} - -//---------------------------------------------------------------------------// -// Explicit instantiation of 'applyInitialConditions' -template void InitialConditionManager::applyInitialConditions( - const std::integral_constant&, - const PhysicsManager& physics_manager, - Teuchos::RCP>& x, - Teuchos::RCP>& x_dot) const; - -template void InitialConditionManager::applyInitialConditions( - const std::integral_constant&, - const PhysicsManager& physics_manager, - Teuchos::RCP>& x, - Teuchos::RCP>& x_dot) const; - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD diff --git a/src/drivers/VertexCFD_InitialConditionManager.hpp b/src/drivers/VertexCFD_InitialConditionManager.hpp deleted file mode 100644 index c7c1795..0000000 --- a/src/drivers/VertexCFD_InitialConditionManager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITIONMANAGER_HPP -#define VERTEXCFD_INITIALCONDITIONMANAGER_HPP - -#include "VertexCFD_MeshManager.hpp" -#include "VertexCFD_PhysicsManager.hpp" - -#include "initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp" -#include "mesh/VertexCFD_Mesh_Restart.hpp" -#include "parameters/VertexCFD_ParameterDatabase.hpp" - -#include - -#include - -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -class InitialConditionManager -{ - public: - InitialConditionManager( - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager); - - double initialTime() const; - - template - void - applyInitialConditions(const std::integral_constant&, - const PhysicsManager& physics_manager, - Teuchos::RCP>& x, - Teuchos::RCP>& x_dot) const; - - private: - Teuchos::RCP _parameter_db; - Teuchos::RCP _mesh_manager; - Teuchos::RCP _restart_reader; - bool _do_restart; - double _t_init; -}; - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITIONMANAGER_HPP diff --git a/src/drivers/VertexCFD_MeshManager.cpp b/src/drivers/VertexCFD_MeshManager.cpp deleted file mode 100644 index bce89af..0000000 --- a/src/drivers/VertexCFD_MeshManager.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "VertexCFD_MeshManager.hpp" -#include "mesh/VertexCFD_Mesh_StkReaderFactory.hpp" - -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -MeshManager::MeshManager(const Parameter::ParameterDatabase& parameter_db, - const Teuchos::RCP>& comm) - : _comm(comm) -{ - auto mesh_params = parameter_db.meshParameters(); - - if ("File" == mesh_params->get("Mesh Input Type")) - { - _mesh_factory = Teuchos::rcp(new VertexCFD::Mesh::StkReaderFactory()); - auto file_params - = Teuchos::parameterList(mesh_params->sublist("File")); - _mesh_factory->setParameterList(file_params); - } - else if ("Inline" == mesh_params->get("Mesh Input Type")) - { - auto inline_params - = Teuchos::parameterList(mesh_params->sublist("Inline")); - - // Initialize integer to store mesh dimension - int mesh_dimension = 0; - - if ("Tri3" == inline_params->get("Element Type")) - { - _mesh_factory - = Teuchos::rcp(new panzer_stk::SquareTriMeshFactory()); - mesh_dimension = 2; - } - else if ("Quad4" == inline_params->get("Element Type")) - { - _mesh_factory - = Teuchos::rcp(new panzer_stk::SquareQuadMeshFactory()); - mesh_dimension = 2; - } - else if ("Tet4" == inline_params->get("Element Type")) - { - _mesh_factory = Teuchos::rcp(new panzer_stk::CubeTetMeshFactory()); - mesh_dimension = 3; - } - else if ("Hex8" == inline_params->get("Element Type")) - { - _mesh_factory = Teuchos::rcp(new panzer_stk::CubeHexMeshFactory()); - mesh_dimension = 3; - } - else - { - throw std::runtime_error( - "Invalid inline element type. Valid options are " - "'Tri3', " - "'Quad4', 'Tet4' and 'Hex8'"); - } - auto inline_mesh_params - = Teuchos::parameterList(inline_params->sublist("Mesh")); - - if (mesh_dimension > 1) - { - inline_mesh_params->set("X Procs", -1); - inline_mesh_params->set("Y Procs", -1); - } - if (mesh_dimension > 2) - { - inline_mesh_params->set("Z Procs", -1); - } - _mesh_factory->setParameterList(inline_mesh_params); - } - else - { - throw std::runtime_error( - "Invalid mesh input type. Valid options are 'File' and 'Inline'"); - } - - _mesh = _mesh_factory->buildUncommitedMesh(Teuchos::getRawMpiComm(*_comm)); -} - -//---------------------------------------------------------------------------// -void MeshManager::completeMeshConstruction() -{ - _mesh_factory->completeMeshConstruction(*_mesh, - Teuchos::getRawMpiComm(*_comm)); - _conn_manager = Teuchos::rcp(new panzer_stk::STKConnManager(_mesh)); -} - -//---------------------------------------------------------------------------// -Teuchos::RCP> MeshManager::comm() const -{ - return _comm; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP MeshManager::mesh() const -{ - return _mesh; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -MeshManager::connectivityManager() const -{ - return _conn_manager; -} - -//---------------------------------------------------------------------------// -int MeshManager::spaceDimension() const -{ - return static_cast(_mesh->getDimension()); -} - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD diff --git a/src/drivers/VertexCFD_MeshManager.hpp b/src/drivers/VertexCFD_MeshManager.hpp deleted file mode 100644 index 86b3500..0000000 --- a/src/drivers/VertexCFD_MeshManager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_MESHMANAGER_HPP -#define VERTEXCFD_MESHMANAGER_HPP - -#include "parameters/VertexCFD_ParameterDatabase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -class MeshManager -{ - public: - MeshManager(const Parameter::ParameterDatabase& parameter_db, - const Teuchos::RCP>& comm); - - void completeMeshConstruction(); - - Teuchos::RCP> comm() const; - Teuchos::RCP mesh() const; - Teuchos::RCP connectivityManager() const; - - int spaceDimension() const; - - private: - Teuchos::RCP> _comm; - Teuchos::RCP _mesh_factory; - Teuchos::RCP _mesh; - Teuchos::RCP _conn_manager; -}; - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_MESHMANAGER_HPP diff --git a/src/drivers/VertexCFD_PhysicsManager.cpp b/src/drivers/VertexCFD_PhysicsManager.cpp deleted file mode 100644 index b1332bd..0000000 --- a/src/drivers/VertexCFD_PhysicsManager.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#include "VertexCFD_PhysicsManager.hpp" -#include "boundary_conditions/VertexCFD_BCStrategy_Factory.hpp" -#include "closure_models/VertexCFD_ClosureModelFactory_TemplateBuilder.hpp" -#include "equation_sets/VertexCFD_EquationSet_Factory.hpp" -#include "linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -template -PhysicsManager::PhysicsManager( - const std::integral_constant&, - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager, - const double initial_time) - : _parameter_db(parameter_db) - , _mesh_manager(mesh_manager) - , _t_init(initial_time) - , _global_data(panzer::createGlobalData()) - , _integration_order(-1) -{ - // Initialize 'num_space_dim' with template value - constexpr int num_space_dim = NumSpaceDim; - - // Get physics communicator. - auto comm = _mesh_manager->comm(); - - // Get user parameters. - auto user_params = _parameter_db->userParameters(); - - // Map element blocks to physics blocks. - auto mesh = _mesh_manager->mesh(); - const auto block_mapping_params = _parameter_db->blockMappingParameters(); - std::map block_ids_to_physics_ids; - panzer::buildBlockIdToPhysicsIdMap(block_ids_to_physics_ids, - *block_mapping_params); - std::map> - block_ids_to_cell_topo; - for (auto itr(block_ids_to_physics_ids.begin()); - itr != block_ids_to_physics_ids.end(); - ++itr) - { - block_ids_to_cell_topo[itr->first] = mesh->getCellTopology(itr->first); - } - - // Get physics parameters. - const auto physics_params = _parameter_db->physicsParameters(); - - // Build the physics blocks. Create defaults for some options that may be - // overwritten by inputs. - const int workset_size = user_params->get("Workset Size"); - const int default_integration_order = 2; - const bool build_transient_support = true; - const std::vector tangent_param_names; - _eq_set_factory = Teuchos::rcp(new VertexCFD::EquationSet::Factory); - panzer::buildPhysicsBlocks(block_ids_to_physics_ids, - block_ids_to_cell_topo, - physics_params, - default_integration_order, - workset_size, - _eq_set_factory, - _global_data, - build_transient_support, - _physics_blocks, - tangent_param_names); - - // FIXME: Everything breaks if our integration order is not consistent, so - // just extract the actaul order from the frist physics block for - // use everywhere else. - _integration_order - = _physics_blocks.at(0)->getIntegrationRules().cbegin()->first; - - // Generate field data to complete the mesh. - const int num_physics_blocks = _physics_blocks.size(); - for (int i = 0; i < num_physics_blocks; ++i) - { - // Get a unique list of the fields names needed by the physics and add - // them to the database. - auto pb = _physics_blocks[i]; - const auto& block_fields = pb->getProvidedDOFs(); - std::set field_names( - block_fields.begin(), block_fields.end()); - for (auto field = field_names.begin(); field != field_names.end(); - ++field) - { - mesh->addSolutionField(field->first, pb->elementBlockID()); - } - } - _mesh_manager->completeMeshConstruction(); - // Generate connectivity and DOF managers. - auto conn_manager = _mesh_manager->connectivityManager(); - panzer::DOFManagerFactory dof_manager_factory; - _dof_manager = dof_manager_factory.buildGlobalIndexer( - Teuchos::opaqueWrapper(Teuchos::getRawMpiComm(*comm)), - _physics_blocks, - conn_manager); - - // Toggle on linear algebra type to construct linear object factory - const std::string lin_alg_type - = user_params->get("Linear Algebra Type", "Tpetra"); - if (lin_alg_type == "Tpetra") - { - _linear_object_factory = Teuchos::rcp( - new panzer::TpetraLinearObjFactory( - comm, _dof_manager)); - } - else if (lin_alg_type == "Epetra") - { - _linear_object_factory = Teuchos::rcp( - new panzer::BlockedEpetraLinearObjFactory( - comm, _dof_manager)); - } - else - { - throw std::runtime_error( - "Invalid linear algebra type. Valid options are 'Tpetra' and " - "'Epetra'"); - } - - // Linear solver factory. - auto linear_solver_params = parameter_db->linearSolverParameters(); - auto lows_factory = VertexCFD::LinearSolvers::LOWSFactoryBuilder::buildLOWS( - linear_solver_params); - - // Create boundary conditions. - auto bc_params = _parameter_db->boundaryConditionParameters(); - panzer::buildBCs(_boundary_conditions, *bc_params, _global_data); - - // Create model evaluator. - _model_evaluator = Teuchos::rcp( - new panzer::ModelEvaluator(_linear_object_factory, - lows_factory, - _global_data, - build_transient_support, - _t_init)); - - // Create additional factories. - _bc_factory = Teuchos::rcp( - new VertexCFD::BoundaryCondition::Factory()); - VertexCFD::ClosureModel::FactoryTemplateBuilder cm_builder; - _cm_factory = Teuchos::rcp( - new panzer::ClosureModelFactory_TemplateManager()); - _cm_factory->buildObjects(cm_builder); -} - -//---------------------------------------------------------------------------// -// Explicit instantiation of the constructor 'PhysicsManager' -template PhysicsManager::PhysicsManager( - const std::integral_constant&, - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager, - const double initial_time); - -template PhysicsManager::PhysicsManager( - const std::integral_constant&, - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager, - const double initial_time); - -//---------------------------------------------------------------------------// -// Add a scalar parameter and an initial value for the parameter. -int PhysicsManager::addScalarParameter(const std::string& name, - const double value) -{ - int param_id = _model_evaluator->addParameter(name, value); - _parameter_indices.emplace(name, param_id); - return param_id; -} - -//---------------------------------------------------------------------------// -// Get the index of a parameter with the given name. -int PhysicsManager::getParameterIndex(const std::string& name) const -{ - auto iter = _parameter_indices.find(name); - if (iter == _parameter_indices.end()) - { - throw std::runtime_error("Parameter not found"); - } - return iter->second; -} - -//---------------------------------------------------------------------------// -void PhysicsManager::setupModel() -{ - // Create worksets. - auto user_params = _parameter_db->userParameters(); - auto mesh = _mesh_manager->mesh(); - auto workset_factory = Teuchos::rcp(new panzer_stk::WorksetFactory(mesh)); - _workset_container = Teuchos::rcp(new panzer::WorksetContainer); - _workset_container->setFactory(workset_factory); - const int num_physics_blocks = _physics_blocks.size(); - for (int i = 0; i < num_physics_blocks; ++i) - { - auto pb = _physics_blocks[i]; - _workset_container->setNeeds(pb->elementBlockID(), - pb->getWorksetNeeds()); - } - const int workset_size = user_params->get("Workset Size"); - _workset_container->setWorksetSize(workset_size); - _workset_container->setGlobalIndexer(_dof_manager); - - // Turn on shared memory versions of Panzer kernels when available - panzer::HP::inst().setUseSharedMemory(true, true); - - // Setup model. - auto closure_params = _parameter_db->closureModelParameters(); - const bool write_graph = user_params->get("Output Graph"); - user_params->set>("MeshManager", - _mesh_manager); - _model_evaluator->setupModel(_workset_container, - _physics_blocks, - _boundary_conditions, - *_eq_set_factory, - *_bc_factory, - *_cm_factory, - *_cm_factory, - *closure_params, - *user_params, - write_graph, - ""); - - // Add scalar parameters. - auto scalar_params = _parameter_db->scalarParameters(); - if (Teuchos::nonnull(scalar_params)) - { - for (auto sp = scalar_params->begin(); sp != scalar_params->end(); ++sp) - { - auto list = scalar_params->sublist(sp->first); - addScalarParameter(list.get("Name"), - list.get("Nominal Value")); - } - } -} - -//---------------------------------------------------------------------------// -Teuchos::RCP PhysicsManager::meshManager() const -{ - return _mesh_manager; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP PhysicsManager::globalData() const -{ - return _global_data; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -PhysicsManager::equationSetFactory() const -{ - return _eq_set_factory; -} - -//---------------------------------------------------------------------------// -const std::vector>& -PhysicsManager::physicsBlocks() const -{ - return _physics_blocks; -} - -//---------------------------------------------------------------------------// -int PhysicsManager::integrationOrder() const -{ - return _integration_order; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP PhysicsManager::dofManager() const -{ - return _dof_manager; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP> -PhysicsManager::linearObjectFactory() const -{ - return _linear_object_factory; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP PhysicsManager::worksetContainer() const -{ - return _workset_container; -} - -//---------------------------------------------------------------------------// -const std::vector& PhysicsManager::boundaryConditions() const -{ - return _boundary_conditions; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -PhysicsManager::boundaryConditionFactory() const -{ - return _bc_factory; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP> -PhysicsManager::closureModelFactory() const -{ - return _cm_factory; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP> -PhysicsManager::modelEvaluator() const -{ - return _model_evaluator; -} - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD diff --git a/src/drivers/VertexCFD_PhysicsManager.hpp b/src/drivers/VertexCFD_PhysicsManager.hpp deleted file mode 100644 index 3a8e1ed..0000000 --- a/src/drivers/VertexCFD_PhysicsManager.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef VERTEXCFD_PHYSICSMANAGER_HPP -#define VERTEXCFD_PHYSICSMANAGER_HPP - -#include "VertexCFD_MeshManager.hpp" - -#include "parameters/VertexCFD_ParameterDatabase.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -class PhysicsManager -{ - public: - template - PhysicsManager(const std::integral_constant&, - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager, - const double initial_time = 0.0); - - // Add a scalar parameter and an initial value for the parameter. Return - // the parameter index. - int addScalarParameter(const std::string& name, const double value); - - // Get the index of a parameter with the given name. - int getParameterIndex(const std::string& name) const; - - // Setup the model after all physics responses and parameters have been - // added. - void setupModel(); - - // Data accessors. - Teuchos::RCP meshManager() const; - Teuchos::RCP globalData() const; - Teuchos::RCP equationSetFactory() const; - const std::vector>& - physicsBlocks() const; - int integrationOrder() const; - Teuchos::RCP dofManager() const; - Teuchos::RCP> - linearObjectFactory() const; - Teuchos::RCP worksetContainer() const; - const std::vector& boundaryConditions() const; - Teuchos::RCP boundaryConditionFactory() const; - Teuchos::RCP> - closureModelFactory() const; - Teuchos::RCP> modelEvaluator() const; - - private: - Teuchos::RCP _parameter_db; - Teuchos::RCP _mesh_manager; - double _t_init; - Teuchos::RCP _global_data; - Teuchos::RCP _eq_set_factory; - std::vector> _physics_blocks; - int _integration_order; - Teuchos::RCP _dof_manager; - Teuchos::RCP> _linear_object_factory; - Teuchos::RCP _workset_container; - std::vector _boundary_conditions; - Teuchos::RCP _bc_factory; - Teuchos::RCP> - _cm_factory; - Teuchos::RCP> _model_evaluator; - std::unordered_map _parameter_indices; -}; - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_PHYSICSMANAGER_HPP diff --git a/src/drivers/unit_test/CMakeLists.txt b/src/drivers/unit_test/CMakeLists.txt deleted file mode 100644 index 3f4b18f..0000000 --- a/src/drivers/unit_test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -configure_file( VertexCFD_DriverUnitTestConfig.hpp.cmakein VertexCFD_DriverUnitTestConfig.hpp ) - -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - MeshManager - PhysicsManager - InitialConditionManager - ) diff --git a/src/drivers/unit_test/VertexCFD_DriverUnitTestConfig.hpp.cmakein b/src/drivers/unit_test/VertexCFD_DriverUnitTestConfig.hpp.cmakein deleted file mode 100644 index d63b08e..0000000 --- a/src/drivers/unit_test/VertexCFD_DriverUnitTestConfig.hpp.cmakein +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef VERTEXCFD_DRIVERUNITTESTCONFIG_HPP -#define VERTEXCFD_DRIVERUNITTESTCONFIG_HPP - -constexpr char VERTEXCFD_DRIVER_TEST_DATA_DIR[] = R"(@CMAKE_SOURCE_DIR@/src/drivers/unit_test/data/)"; -constexpr char VERTEXCFD_DRIVER_TEST_INPUT_DIR[] = R"(@CMAKE_SOURCE_DIR@/examples/inputs/)"; -constexpr char VERTEXCFD_DRIVER_TEST_MESH_DIR[] = R"(@CMAKE_SOURCE_DIR@/examples/mesh/)"; - -#endif // end VERTEXCFD_DRIVERUNITTESTCONFIG_HPP diff --git a/src/drivers/unit_test/data/simple_box_2d_restart.xml b/src/drivers/unit_test/data/simple_box_2d_restart.xml deleted file mode 100644 index 8584b9d..0000000 --- a/src/drivers/unit_test/data/simple_box_2d_restart.xml +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/drivers/unit_test/data/simple_box_fim_3d.xml b/src/drivers/unit_test/data/simple_box_fim_3d.xml deleted file mode 100644 index 127ecfd..0000000 --- a/src/drivers/unit_test/data/simple_box_fim_3d.xml +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/drivers/unit_test/tstInitialConditionManager.cpp b/src/drivers/unit_test/tstInitialConditionManager.cpp deleted file mode 100644 index 6ced86c..0000000 --- a/src/drivers/unit_test/tstInitialConditionManager.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testInitialConditionManager( - const Teuchos::RCP>& comm, - const Teuchos::RCP& parameter_db, - const bool restart = false) -{ - // Initialize mesh dimension - constexpr int num_space_dim = NumSpaceDim; - - // Create the mesh. - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - - // Create physics. - auto physics_manager = Teuchos::rcp( - new PhysicsManager(std::integral_constant{}, - parameter_db, - mesh_manager)); - - // Finish physics. - physics_manager->setupModel(); - - // Create initial conditions. - auto ic_manager = Teuchos::rcp( - new InitialConditionManager(parameter_db, mesh_manager)); - - // Apply initial conditions. - auto x_space = physics_manager->modelEvaluator()->get_x_space(); - Teuchos::RCP> x; - Teuchos::RCP> x_dot; - ic_manager->applyInitialConditions( - std::integral_constant{}, - *physics_manager, - x, - x_dot); - - // Check initial time. - if (restart) - { - EXPECT_EQ(0.01, ic_manager->initialTime()); - } - else - { - EXPECT_EQ(0.0, ic_manager->initialTime()); - } - - // Check initial condition. The "simple_box_Nd.xml" files have initial - // conditions of: \phi_p = 1, u = 1, v = 2, w = 3. We will use the - // 1-norm to verify this. - const int num_node - = mesh_manager->mesh()->getEntityCounts(stk::topology::NODE_RANK); - const double norm_1 = num_node * (0.5 + 3 * (NumSpaceDim - 1)); - EXPECT_EQ(norm_1, Thyra::norm_1(*x)); - EXPECT_EQ(0.0, Thyra::norm_1(*x_dot)); -} - -//---------------------------------------------------------------------------// -template -void InitialConditionManagerND() -{ - // Initialize space dimension - constexpr int num_space_dim = NumSpaceDim; - - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - int argc = 2; - const std::string option = "--i="; - const std::string location = VERTEXCFD_DRIVER_TEST_INPUT_DIR; - const std::string file = "simple_box_" + std::to_string(num_space_dim) - + "d.xml"; - std::string argv_str = option + location + file; - char* argv[2]; - argv[1] = &argv_str[0]; - - // Setup database. - auto parameter_db - = Teuchos::rcp(new Parameter::ParameterDatabase(comm, argc, argv)); - - // Test. - testInitialConditionManager(comm, parameter_db); -} - -//---------------------------------------------------------------------------// -TEST(InitialConditionManager2D, ic_test) -{ - InitialConditionManagerND<2>(); -} - -//---------------------------------------------------------------------------// -TEST(InitialConditionManager3D, ic_test) -{ - InitialConditionManagerND<3>(); -} - -//---------------------------------------------------------------------------// -template -void testRestartMultiD() -{ - // Space dimension - constexpr int num_space_dim = NumSpaceDim; - const std::string num_space_dim_string = std::to_string(num_space_dim); - - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - int argc = 2; - const std::string option = "--i="; - const std::string input_location = VERTEXCFD_DRIVER_TEST_DATA_DIR; - const std::string input_file = "simple_box_" + num_space_dim_string - + "d_restart.xml"; - std::string argv_str = option + input_location + input_file; - char* argv[2]; - argv[1] = &argv_str[0]; - - // Setup database. - auto parameter_db - = Teuchos::rcp(new Parameter::ParameterDatabase(comm, argc, argv)); - - // Update restart data parameters. In this test the initial conditions - // from the previous test were written to the restart file and thus the - // initial conditions and time from restart should be the same. - const std::string data_location = VERTEXCFD_DRIVER_TEST_DATA_DIR; - const std::string data_file = data_location + "simple_box_" - + num_space_dim_string + "d.restart.data"; - const std::string dofmap_file = data_location + "simple_box_" - + num_space_dim_string + "d.restart.dofmap"; - parameter_db->readRestartParameters()->set("Restart Data File Name", - data_file); - parameter_db->readRestartParameters()->set("Restart DOF Map File Name", - dofmap_file); - - testInitialConditionManager(comm, parameter_db, true); -} - -//---------------------------------------------------------------------------// -TEST(InitialConditionManager2D, restart_test) -{ - testRestartMultiD<2>(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/drivers/unit_test/tstMeshManager.cpp b/src/drivers/unit_test/tstMeshManager.cpp deleted file mode 100644 index 592ac44..0000000 --- a/src/drivers/unit_test/tstMeshManager.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -TEST(MeshManager, file_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Make an empty parameter database and build mesh parmaeters. - Parameter::ParameterDatabase parameter_db(comm); - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "File"); - auto& file_params = mesh_params->sublist("File"); - std::string mesh_location = VERTEXCFD_DRIVER_TEST_MESH_DIR; - std::string mesh_file = "test_mesh_manager.exo"; - std::string filepath = mesh_location + mesh_file; - file_params.set("File Name", filepath); - file_params.set("Decomp Method", "RCB"); - - // Create the mesh. - MeshManager mesh_manager(parameter_db, comm); - mesh_manager.completeMeshConstruction(); - - // Check the mesh. - EXPECT_EQ(2, mesh_manager.spaceDimension()); - auto mesh = mesh_manager.mesh(); - EXPECT_EQ(2, mesh->getNumElementBlocks()); - - // Check the connectivity by checking the element block. - auto conn = mesh_manager.connectivityManager(); - EXPECT_EQ(2, conn->numElementBlocks()); -} - -//---------------------------------------------------------------------------// -void testInlineMesh(const std::string element_type) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Mesh dimension based on 'element_type' entry. 'split' is - // multiplicator used to scale the number of elements when - // using triangle or tetrahedral elements - int mesh_dimension = 0; - int split = 1; - if (element_type == "Quad4" || element_type == "Tri3") - { - mesh_dimension = 2; - if (element_type == "Tri3") - split = 2; - } - else if (element_type == "Tet4" || element_type == "Hex8") - { - mesh_dimension = 3; - if (element_type == "Tet4") - split = 12; - } - - // Make an empty parameter database and build mesh parameters. - Parameter::ParameterDatabase parameter_db(comm); - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "Inline"); - auto& inline_params = mesh_params->sublist("Inline"); - inline_params.set("Element Type", element_type); - auto& mesh_details = inline_params.sublist("Mesh"); - const int nelem_x = 3; - const int nelem_y = nelem_x * mesh_dimension; - const int nelem_z = nelem_y * mesh_dimension; - mesh_details.set("X0", 0.0); - mesh_details.set("Xf", 1.0); - mesh_details.set("X Elements", nelem_x); - if (mesh_dimension > 1) - { - mesh_details.set("Y0", 0.0); - mesh_details.set("Yf", 1.0); - mesh_details.set("Y Elements", nelem_y); - } - if (mesh_dimension > 2) - { - mesh_details.set("Z0", 0.0); - mesh_details.set("Zf", 1.0); - mesh_details.set("Z Elements", nelem_z); - } - - // Create the mesh. - MeshManager mesh_manager(parameter_db, comm); - mesh_manager.completeMeshConstruction(); - - // Check the mesh. - EXPECT_EQ(mesh_dimension, mesh_manager.spaceDimension()); - const auto mesh = mesh_manager.mesh(); - EXPECT_EQ(1, mesh->getNumElementBlocks()); - int mesh_num_elem = split * nelem_x; - if (mesh_dimension > 1) - mesh_num_elem *= nelem_y; - if (mesh_dimension > 2) - mesh_num_elem *= nelem_z; - EXPECT_EQ(mesh_num_elem, mesh->getEntityCounts(stk::topology::ELEM_RANK)); - - // Check the connectivity by checking the element block. - auto conn = mesh_manager.connectivityManager(); - EXPECT_EQ(1, conn->numElementBlocks()); -} -//---------------------------------------------------------------------------// -TEST(MeshManager, tri3_test) -{ - testInlineMesh("Tri3"); -} -//---------------------------------------------------------------------------// -TEST(MeshManager, quad4_test) -{ - testInlineMesh("Quad4"); -} -//---------------------------------------------------------------------------// -TEST(MeshManager, tet4_test) -{ - testInlineMesh("Tet4"); -} -//---------------------------------------------------------------------------// -TEST(MeshManager, hex8_test) -{ - testInlineMesh("Hex8"); -} -//---------------------------------------------------------------------------// -TEST(MeshManager, bad_elem_type_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Make an empty parameter database and build mesh parmaeters. - Parameter::ParameterDatabase parameter_db(comm); - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "Inline"); - auto& inline_params = mesh_params->sublist("Inline"); - inline_params.set("Element Type", "BadElem"); - - // Create the mesh. - std::string msg - = "Invalid inline element type. Valid options are 'Tri3', " - "'Quad4', 'Tet4' and 'Hex8'"; - EXPECT_THROW( - try { - MeshManager mesh_manager(parameter_db, comm); - } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// -TEST(MeshManager, bad_mesh_type_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Make an empty parameter database and build mesh parmaeters. - Parameter::ParameterDatabase parameter_db(comm); - auto mesh_params = parameter_db.meshParameters(); - mesh_params->set("Mesh Input Type", "Throw"); - - // Create the mesh. - std::string msg - = "Invalid mesh input type. Valid options are 'File' and 'Inline'"; - EXPECT_THROW( - try { - MeshManager mesh_manager(parameter_db, comm); - } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/drivers/unit_test/tstPhysicsManager.cpp b/src/drivers/unit_test/tstPhysicsManager.cpp deleted file mode 100644 index 63d91b5..0000000 --- a/src/drivers/unit_test/tstPhysicsManager.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -auto createPhysicsManager(const std::string& location, - const std::string& filename) -{ - // Initialize mesh dimension - constexpr int num_space_dim = NumSpaceDim; - - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - int argc = 2; - const std::string option = "--i="; - std::string argv_str = option + location + filename; - char* argv[2]; - argv[1] = &argv_str[0]; - - // Setup database. - auto parameter_db - = Teuchos::rcp(new Parameter::ParameterDatabase(comm, argc, argv)); - - // Create the mesh. - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - - // Create physics. - double t_init = 1.3; - auto physics_manager = Teuchos::rcp( - new PhysicsManager(std::integral_constant{}, - parameter_db, - mesh_manager, - t_init)); - - // Finish physics. - physics_manager->setupModel(); - - return physics_manager; -} - -//---------------------------------------------------------------------------// -TEST(PhysicsManager, manager_test) -{ - auto physics_manager = createPhysicsManager<2>( - VERTEXCFD_DRIVER_TEST_INPUT_DIR, "simple_box_2d.xml"); - - // Check data. - EXPECT_TRUE(Teuchos::nonnull(physics_manager->globalData())); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->equationSetFactory())); - EXPECT_EQ(1, physics_manager->physicsBlocks().size()); - EXPECT_EQ(2, physics_manager->integrationOrder()); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->dofManager())); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->linearObjectFactory())); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->worksetContainer())); - EXPECT_EQ(4, physics_manager->boundaryConditions().size()); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->boundaryConditionFactory())); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->closureModelFactory())); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->modelEvaluator())); -} - -//---------------------------------------------------------------------------// -// Test boundary factory model of incompressible NS equations with full -// induction MHD model enabled using a dummy input file for a 3D geometry. -TEST(PhysicsManagerFIM, manager_test) -{ - auto physics_manager = createPhysicsManager<3>( - VERTEXCFD_DRIVER_TEST_DATA_DIR, "simple_box_fim_3d.xml"); - // Check bouondary logic. - EXPECT_EQ(6, physics_manager->boundaryConditions().size()); - EXPECT_TRUE(Teuchos::nonnull(physics_manager->boundaryConditionFactory())); -} - -//---------------------------------------------------------------------------// -template -void testScalarParameter() -{ - // Create physics manager. - auto physics_manager = createPhysicsManager<2>( - VERTEXCFD_DRIVER_TEST_INPUT_DIR, "simple_box_2d.xml"); - - // Add a scalar parameter. - const std::string scalar_name = "scalar_param"; - const double param_value = 2.03; - auto param_id - = physics_manager->addScalarParameter(scalar_name, param_value); - EXPECT_EQ(0, param_id); - EXPECT_EQ(0, physics_manager->getParameterIndex(scalar_name)); - - // Finish physics. - physics_manager->setupModel(); - - // Check scalar parameter. - auto param_lib = physics_manager->globalData()->pl; - EXPECT_EQ(param_value, param_lib->getValue(scalar_name)); -} -//---------------------------------------------------------------------------// -TEST(PhysicsManager, scalar_parameter_test_residual) -{ - testScalarParameter(); -} - -//---------------------------------------------------------------------------// -TEST(PhysicsManager, scalar_parameter_test_jacobian) -{ - testScalarParameter(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/drivers/vertexcfd.cpp b/src/drivers/vertexcfd.cpp deleted file mode 100644 index dbf4d0d..0000000 --- a/src/drivers/vertexcfd.cpp +++ /dev/null @@ -1,528 +0,0 @@ -// Due to a conflict with FAD types and Kokkos views, this file needs to be -// included before other VertexCFD includes -#include "utils/VertexCFD_Utils_KokkosFadFixup.hpp" - -#include "VertexCFD_ExternalFieldsManager.hpp" -#include "VertexCFD_InitialConditionManager.hpp" -#include "VertexCFD_MeshManager.hpp" -#include "VertexCFD_PhysicsManager.hpp" - -#include "mesh/VertexCFD_Mesh_ExodusWriter.hpp" -#include "mesh/VertexCFD_Mesh_Restart.hpp" -#include "observers/VertexCFD_Compute_ErrorNorms.hpp" -#include "observers/VertexCFD_Compute_Volume.hpp" -#include "observers/VertexCFD_NOXObserver_IterationOutput.hpp" -#include "observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp" -#include "observers/VertexCFD_TempusObserver_IterationOutput.hpp" -#include "observers/VertexCFD_TempusObserver_ResponseOutput.hpp" -#include "observers/VertexCFD_TempusObserver_WriteMatrix.hpp" -#include "observers/VertexCFD_TempusObserver_WriteRestart.hpp" -#include "observers/VertexCFD_TempusObserver_WriteToExodus.hpp" -#include "observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp" -#include "observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp" -#include "observers/VertexCFD_TempusTimeStepControl_Strategy.hpp" -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "responses/VertexCFD_ResponseManager.hpp" -#include "responses/VertexCFD_Response_Utils.hpp" - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -//---------------------------------------------------------------------------// -// Parse input file and initialize mesh manager -void parse_input_file_and_mesh( - int argc, - char* argv[], - const Teuchos::RCP>& comm, - Teuchos::RCP& parameter_db, - Teuchos::RCP& mesh_manager) -{ - // Parse input file specified in 'argc' and store its content in - // 'parameter_db' - parameter_db = Teuchos::rcp( - new VertexCFD::Parameter::ParameterDatabase(comm, argc, argv)); - - // Initialize mesh manager - mesh_manager - = Teuchos::rcp(new VertexCFD::MeshManager(*parameter_db, comm)); -} - -//---------------------------------------------------------------------------// -// Run VertexCFD after parsing the input file and initializinig the mesh -// manager object in function 'parse_input_file_and_mesh'. -template -void run_vertexcfd( - const Teuchos::RCP>& comm, - const Teuchos::RCP& parameter_db, - const Teuchos::RCP& mesh_manager) -{ - // Setup an output stream that will only write to rank 0. - Teuchos::FancyOStream ostream(Teuchos::rcpFromRef(std::cout)); - ostream.setShowProcRank(false); - ostream.setOutputToRootOnly(0); - - // Get sublists. - auto solver_params = parameter_db->transientSolverParameters(); - auto closure_params = parameter_db->closureModelParameters(); - auto response_output_params = parameter_db->responseOutputParameters(); - auto user_params = parameter_db->userParameters(); - auto output_params = parameter_db->outputParameters(); - auto write_restart_params = parameter_db->writeRestartParameters(); - auto write_matrix_params = parameter_db->writeMatrixParameters(); - auto profiling_params = parameter_db->profilingParameters(); - - // Setup timers. - bool use_timers = Teuchos::nonnull(profiling_params); - Teuchos::RCP stacked_timer; - if (use_timers) - { - stacked_timer = Teuchos::rcp(new Teuchos::StackedTimer("VertexCFD")); - Teuchos::TimeMonitor::setStackedTimer(stacked_timer); - stacked_timer->start("Physics"); - } - - // Used for template argument deduction in constructors below. - constexpr auto num_space_dim = std::integral_constant{}; - - // Create external field evaluator if needed - if (user_params->isType("External Field Parameter File")) - { - const std::string ef_filename - = user_params->get("External Field Parameter File"); - auto external_fields_manager = Teuchos::rcp( - new VertexCFD::ExternalFieldsManager( - num_space_dim, comm, ef_filename)); - user_params->set("External Fields Manager", external_fields_manager); - } - - // Get mesh object from mesh manager. - auto mesh = mesh_manager->mesh(); - - // Create initial condition manager. - auto ic_manager = Teuchos::rcp( - new VertexCFD::InitialConditionManager(parameter_db, mesh_manager)); - auto t_init = ic_manager->initialTime(); - - // Create physics. - auto physics_manager = Teuchos::rcp(new VertexCFD::PhysicsManager( - num_space_dim, parameter_db, mesh_manager, t_init)); - auto physics = physics_manager->modelEvaluator(); - auto integration_order = physics_manager->integrationOrder(); - - // Setup volume/surface responses. - std::vector response_output_freq; - auto responses = Teuchos::rcp( - new VertexCFD::Response::ResponseManager(physics_manager)); - if (Teuchos::nonnull(response_output_params)) - { - if (response_output_params->numParams() > 0) - { - // Allow setting output frequency, defaulting to once at the end. - const auto default_output_freq = response_output_params->get( - "Output Frequency", std::numeric_limits::max()); - - for (auto param_itr = response_output_params->begin(); - param_itr != response_output_params->end(); - ++param_itr) - { - const auto& name = param_itr->first; - - // Skip over any regular Parameters. - if (!response_output_params->isSublist(name)) - continue; - - auto& plist = response_output_params->sublist(name); - - const auto field_names_list - = plist.get("Field Name"); - std::vector field_names; - panzer::StringTokenizer( - field_names, field_names_list, ",", true); - const int num_fields = field_names.size(); - - // Allow overriding output frequency for this response. - const auto output_freq - = plist.get("Output Frequency", default_output_freq); - - // Get element blocks or sidesets for this response. - const auto workset_descriptors - = VertexCFD::Response::buildWorksetDescriptors(plist); - - // Add the response and save response output frequency - if (plist.isSublist("Probe " - "Coordinates")) - { - const auto probe_list = plist.sublist("Probe Coordinates"); - for (int j = 0; j < num_fields; ++j) - { - for (int i = 0; i < probe_list.numParams(); ++i) - { - const std::string si = std::to_string(i + 1); - const std::string pb_nm = "Probe " + si; - const std::string name_ji = name + " " + si + " - " - + field_names[j]; - const auto point_i - = probe_list.get>(pb_nm); - - responses->addProbeResponse(name_ji, - field_names[j], - point_i, - workset_descriptors); - - response_output_freq.emplace_back(output_freq); - } - } - } - else - { - for (int j = 0; j < num_fields; ++j) - { - const std::string name_j = name + " - " - + field_names[j]; - if (std::string::npos != name.find("Max")) - { - responses->addMaxValueResponse( - name_j, field_names[j], workset_descriptors); - } - else if (std::string::npos != name.find("Min")) - { - responses->addMinValueResponse( - name_j, field_names[j], workset_descriptors); - } - else - { - responses->addFunctionalResponse( - name_j, field_names[j], workset_descriptors); - } - response_output_freq.emplace_back(output_freq); - } - } - } - } - } - - // Setup time step control. This adds a response, so must be built before - // calling setupModel. - Teuchos::RCP> dt_strategy; - if (user_params->isParameter("CFL")) - { - dt_strategy = Teuchos::rcp( - new VertexCFD::TempusTimeStepControl::GlobalCFL( - *user_params, physics_manager)); - } - else - { - dt_strategy = Teuchos::rcp( - new VertexCFD::TempusTimeStepControl::GlobalTimeStep( - *user_params, physics_manager)); - } - - // Setup the model after the physics responses have been added. - physics_manager->setupModel(); - auto workset_container = physics_manager->worksetContainer(); - auto dof_manager = physics_manager->dofManager(); - auto linear_object_factory = physics_manager->linearObjectFactory(); - auto cm_factory = physics_manager->closureModelFactory(); - auto physics_blocks = physics_manager->physicsBlocks(); - - // Create io response library. - Teuchos::RCP exodus_writer; - if (Teuchos::nonnull(output_params)) - { - auto io_response_library - = Teuchos::rcp(new panzer::ResponseLibrary( - workset_container, dof_manager, linear_object_factory)); - - // Make sure these io sublists exist. - output_params->sublist("Cell Average Quantities"); - output_params->sublist("Cell Average Vectors"); - output_params->sublist("Cell Quantities"); - output_params->sublist("Nodal Quantities"); - - // Setup solution output. This must occur before we build evaluators in - // the response library so this observer may register its evaluators. - exodus_writer = Teuchos::rcp( - new VertexCFD::Mesh::ExodusWriter(mesh, - dof_manager, - linear_object_factory, - io_response_library, - *output_params)); - - // Create io response evaluators. - panzer_stk::IOClosureModelFactory_TemplateBuilder - io_cm_builder(*cm_factory, mesh, *output_params); - panzer::ClosureModelFactory_TemplateManager io_cm_factory; - io_cm_factory.buildObjects(io_cm_builder); - io_response_library->buildResponseEvaluators( - physics_blocks, io_cm_factory, *closure_params, *user_params); - } - - // Set initial conditions. - Teuchos::RCP> solution; - Teuchos::RCP> solution_dot; - ic_manager->applyInitialConditions( - num_space_dim, *physics_manager, solution, solution_dot); - - // Setup iteration output observers. - Teuchos::RCP nox_iteration_observer - = Teuchos::rcp(new VertexCFD::NOXObserver::IterationOutput()); - solver_params->sublist("Default Stepper") - .sublist("Default Solver") - .sublist("NOX") - .sublist("Solver Options") - .set("User Defined Pre/Post Operator", nox_iteration_observer); - - // Setup time integrator -- toggle interface on Trilinos version -#if TRILINOS_MAJOR_MINOR_VERSION >= 130100 - // Remove Tempus entries that are deprecated in Trilinos 13.2 - auto integrator_params - = Teuchos::sublist(solver_params, "Default Integrator"); - auto tsc_params = Teuchos::sublist(integrator_params, "Time Step Control"); - tsc_params->remove("Minimum Order", false); - tsc_params->remove("Maximum Order", false); - tsc_params->remove("Initial Order", false); - tsc_params->remove("Integrator Step Type", false); - auto integrator - = Tempus::createIntegratorBasic(solver_params, physics); -#else - auto integrator = Tempus::integratorBasic(solver_params, physics); -#endif - - // Build a composite observer containing all of our tempus observers. - auto integrator_observer - = Teuchos::rcp(new Tempus::IntegratorObserverComposite()); - - // Setup exodus output observer. - if (Teuchos::nonnull(output_params)) - { - auto exodus_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::WriteToExodus( - exodus_writer, *output_params)); - integrator_observer->addObserver(exodus_observer); - } - - // Error norm response library - auto error_norm_response_library - = Teuchos::rcp(new panzer::ResponseLibrary( - workset_container, dof_manager, linear_object_factory)); - - const bool error_norm_flag = user_params->isSublist("Compute Error Norms"); - if (error_norm_flag) - { - const auto bcs = physics_manager->boundaryConditions(); - const auto bc_factory = physics_manager->boundaryConditionFactory(); - const auto eq_set_factory = physics_manager->equationSetFactory(); - const auto error_norm_list - = user_params->sublist("Compute Error Norms"); - - // Compute Volume integration when Compute Error Norm - auto volume = Teuchos::rcp(new VertexCFD::ComputeVolume::Volume( - mesh, - linear_object_factory, - error_norm_response_library, - physics_blocks, - *cm_factory, - *closure_params, - *user_params, - workset_container, - bcs, - bc_factory, - eq_set_factory, - integration_order)); - volume->ComputeVol(); - const double volume_value = volume->volume(); - - // L1/L2 Error norm instance - auto comp_error_norms = Teuchos::rcp( - new VertexCFD::ComputeErrorNorms::ErrorNorms( - mesh, - linear_object_factory, - error_norm_response_library, - physics_blocks, - *cm_factory, - *closure_params, - *user_params, - eq_set_factory, - volume_value, - integration_order)); - - // Set error norm output observer. - auto tempus_error_norm_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::ErrorNormOutput( - error_norm_list, comp_error_norms)); - integrator_observer->addObserver(tempus_error_norm_observer); - } - - // Set iteration output observer. - auto tempus_iteration_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::IterationOutput(dt_strategy)); - integrator_observer->addObserver(tempus_iteration_observer); - - // Set response output observer. - if (Teuchos::nonnull(response_output_params)) - { - auto tempus_response_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::ResponseOutput( - responses, response_output_freq)); - integrator_observer->addObserver(tempus_response_observer); - } - - // Setup restart observer. - if (Teuchos::nonnull(write_restart_params)) - { - bool write_restart = false; - if (write_restart_params->isType("Write Restart")) - { - write_restart = write_restart_params->get("Write Restart"); - } - if (write_restart) - { - auto restart_writer - = Teuchos::rcp(new VertexCFD::Mesh::RestartWriter( - mesh, dof_manager, *write_restart_params)); - auto restart_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::WriteRestart( - restart_writer, *write_restart_params)); - integrator_observer->addObserver(restart_observer); - } - } - - // Set up matrix write observer - if (Teuchos::nonnull(write_matrix_params)) - { - bool write_matrix = false; - if (write_matrix_params->isType("Write Matrix")) - { - write_matrix = write_matrix_params->get("Write Matrix"); - } - if (write_matrix) - { - auto tempus_writematrix_observer = Teuchos::rcp( - new VertexCFD::TempusObserver::WriteMatrix( - *write_matrix_params)); - integrator_observer->addObserver(tempus_writematrix_observer); - } - } - - // Give our composite observer to the integrator. - integrator->setObserver(integrator_observer); - - // Initialize integrator. - integrator->initialize(); - - // Setup time step control. - auto tsc = integrator->getNonConstTimeStepControl(); - tsc->setTimeStepControlStrategy(dt_strategy); - - // Initialize solution. - integrator->initializeSolutionHistory(t_init, solution, solution_dot); - - // Solve. - integrator->advanceTime(); - - // Output timing. - if (use_timers) - { - stacked_timer->stop("Physics"); - Teuchos::StackedTimer::OutputOptions options; - options.output_fraction = true; - options.output_total_updates = true; - options.output_minmax = true; - options.output_proc_minmax = true; - options.align_columns = true; - options.print_names_before_values = true; - options.output_histogram = false; - options.max_levels = 100; - options.drop_time = -1.0; - if (profiling_params->isType("Max Output Levels")) - { - options.max_levels - = profiling_params->get("Max Output Levels"); - } - if (profiling_params->isType("Minimum Cutoff")) - { - options.drop_time - = profiling_params->get("Minimum Cutoff"); - } - stacked_timer->report(std::cout, comm, options); - } -} - -//---------------------------------------------------------------------------// -// Main function -int main(int argc, char* argv[]) -{ - // Start MPI. Panzer and other Trilinos components want the Teuchos MPI - // environment initialized so we need to do this here. We can still get - // raw MPI communicators as needed. - Teuchos::GlobalMPISession mpi_session(&argc, &argv, nullptr); - - // Kokkos scopeguard (initialize and finalize) - Kokkos::ScopeGuard kokkos(argc, argv); - - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input file and initialize mesh manager - Teuchos::RCP parameter_db; - Teuchos::RCP mesh_manager; - parse_input_file_and_mesh(argc, argv, comm, parameter_db, mesh_manager); - - // Get mesh dimension from mesh manager - const int mesh_dimension = mesh_manager->mesh()->getDimension(); - - // Run VertexCFD - switch (mesh_dimension) - { - case 2: - run_vertexcfd<2>(comm, parameter_db, mesh_manager); - break; - case 3: - run_vertexcfd<3>(comm, parameter_db, mesh_manager); - break; - default: - const std::string msg = - "\n\nERROR:\n" - "The mesh dimension read from the Exodus mesh file\n" - "or the inline mesh is found to be '" + - std::to_string(mesh_dimension) + - "'\n. Only 2D and 3D meshes are currently supported.\n" - "Please check your input file.\n"; - throw std::runtime_error(msg); - } - - return 0; -} - -//---------------------------------------------------------------------------// diff --git a/src/equation_sets/VertexCFD_EquationSet_Factory.hpp b/src/equation_sets/VertexCFD_EquationSet_Factory.hpp deleted file mode 100644 index 48731c2..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_Factory.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_EQUATIONSET_FACTORY_HPP -#define VERTEXCFD_EQUATIONSET_FACTORY_HPP - -#include "VertexCFD_EquationSet_Heat.hpp" -#include "VertexCFD_EquationSet_IncompressibleNavierStokes.hpp" - -#include -#include -#include - -namespace VertexCFD -{ -namespace EquationSet -{ -//---------------------------------------------------------------------------// - -PANZER_DECLARE_EQSET_TEMPLATE_BUILDER(Heat, Heat) -PANZER_DECLARE_EQSET_TEMPLATE_BUILDER(IncompressibleNavierStokes, - IncompressibleNavierStokes) - -//---------------------------------------------------------------------------// -class Factory : public panzer::EquationSetFactory -{ - public: - Teuchos::RCP> - buildEquationSet(const Teuchos::RCP& params, - const int& default_integration_order, - const panzer::CellData& cell_data, - const Teuchos::RCP& global_data, - const bool build_transient_support) const override - { - // This variable needs to have this exact name to work with the macro - // called below. - auto eq_set = Teuchos::rcp( - new panzer::EquationSet_TemplateManager); - - // The "found" variable is used in-place in the macro called below. - bool found = false; - - // Call the macro for each equation set and check that we found it. - PANZER_BUILD_EQSET_OBJECTS("Heat", Heat); - PANZER_BUILD_EQSET_OBJECTS("IncompressibleNavierStokes", - IncompressibleNavierStokes); - if (!found) - { - throw std::runtime_error("Equation set not valid"); - } - - // Return the equation set - return eq_set; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace EquationSet -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EQUATIONSET_FACTORY_HPP diff --git a/src/equation_sets/VertexCFD_EquationSet_Heat.cpp b/src/equation_sets/VertexCFD_EquationSet_Heat.cpp deleted file mode 100644 index b88f903..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_Heat.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_EquationSet_Heat.hpp" -#include "VertexCFD_EquationSet_Heat_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL(VertexCFD::EquationSet::Heat) diff --git a/src/equation_sets/VertexCFD_EquationSet_Heat.hpp b/src/equation_sets/VertexCFD_EquationSet_Heat.hpp deleted file mode 100644 index 37e0e13..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_Heat.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef VERTEXCFD_EQUATIONSET_HEAT_HPP -#define VERTEXCFD_EQUATIONSET_HEAT_HPP - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace EquationSet -{ -//---------------------------------------------------------------------------// -// Conductive heat equation -//---------------------------------------------------------------------------// -template -class Heat : public panzer::EquationSet_DefaultImpl -{ - public: - Heat(const Teuchos::RCP& params, - const int& default_integration_order, - const panzer::CellData& cell_data, - const Teuchos::RCP& gd, - const bool build_transient_support); - - void buildAndRegisterEquationSetEvaluators( - PHX::FieldManager& fm, - const panzer::FieldLibrary& field_library, - const Teuchos::ParameterList& user_data) const override; - - std::string fieldName(const int dof) const; - - private: - const std::string _dof_name; -}; - -//---------------------------------------------------------------------------// - -} // end namespace EquationSet -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EQUATIONSET_HEAT_HPP diff --git a/src/equation_sets/VertexCFD_EquationSet_Heat_impl.hpp b/src/equation_sets/VertexCFD_EquationSet_Heat_impl.hpp deleted file mode 100644 index 3d6b277..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_Heat_impl.hpp +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef VERTEXCFD_EQUATIONSET_HEAT_IMPL_HPP -#define VERTEXCFD_EQUATIONSET_HEAT_IMPL_HPP - -#include -#include -#include -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace EquationSet -{ -//---------------------------------------------------------------------------// -template -Heat::Heat(const Teuchos::RCP& params, - const int& default_integration_order, - const panzer::CellData& cell_data, - const Teuchos::RCP& global_data, - const bool build_transient_support) - : panzer::EquationSet_DefaultImpl(params, - default_integration_order, - cell_data, - global_data, - build_transient_support) - , _dof_name("temperature") -{ - // This equation set need not always be transient. Could solve Poisson - if (!this->buildTransientSupport()) - { - throw std::logic_error("Heat equation requires transient support"); - } - - // Set default parameter values and validate the inputs. - Teuchos::ParameterList valid_parameters; - this->setDefaultValidParameters(valid_parameters); - valid_parameters.set( - "Model ID", "", "Closure model id associated with this equation set"); - valid_parameters.set("Basis Order", 1, "Order of the basis"); - valid_parameters.set("Integration Order", 2, "Order of the integration"); - params->validateParametersAndSetDefaults(valid_parameters); - - // Extract parameters. - const int basis_order = params->get("Basis Order", 1); - const int integration_order - = params->get("Integration Order", basis_order + 1); - const auto model_id = params->get("Model ID"); - - // Get the number of space dimensions. - const auto num_space_dim = cell_data.baseCellDimension(); - if (!(num_space_dim == 2 || num_space_dim == 3)) - { - throw std::runtime_error("Number of space dimensions not supported"); - } - - this->addDOF(_dof_name, "HGrad", basis_order, integration_order); - this->addDOFGrad(_dof_name); - if (this->buildTransientSupport()) - { - this->addDOFTimeDerivative(_dof_name); - } - this->addClosureModel(model_id); - this->setupDOFs(); -} - -//---------------------------------------------------------------------------// -template -void Heat::buildAndRegisterEquationSetEvaluators( - PHX::FieldManager& fm, - const panzer::FieldLibrary&, - const Teuchos::ParameterList&) const -{ - const auto ir = this->getIntRuleForDOF(_dof_name); - const auto basis = this->getBasisIRLayoutForDOF(_dof_name); - - std::vector term_names; - - if (this->buildTransientSupport()) - { - const std::string term_name{"RESIDUAL_" + _dof_name + "_TRANSIENT_OP"}; - term_names.push_back(term_name); - const auto op{Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - term_name, - "DXDT_" + _dof_name, - *basis, - *ir, - 1.0))}; - this->template registerEvaluator(fm, op); - } - - { - const std::string term_name{"RESIDUAL_" + _dof_name - + "_CONDUCTIVE_TERM"}; - term_names.push_back(term_name); - const std::vector field_multipliers{ - "thermal_conductivity"}; - const auto op{Teuchos::rcp( - new panzer::Integrator_GradBasisDotVector( - panzer::EvaluatorStyle::EVALUATES, - term_name, - "GRAD_" + _dof_name, - *basis, - *ir, - 1.0, - field_multipliers))}; - this->template registerEvaluator(fm, op); - } - - { - const auto term_name{"RESIDUAL_" + _dof_name + "_SOURCE_OP"}; - const auto op{Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - term_name, - "SOURCE_" + _dof_name, - *basis, - *ir, - -1.0))}; - this->template registerEvaluator(fm, op); - } - - this->buildAndRegisterResidualSummationEvaluator(fm, _dof_name, term_names); -} - -//---------------------------------------------------------------------------/ -template -std::string Heat::fieldName(const int dof) const -{ - if (dof > 0) - { - throw std::logic_error("Heat equation contributes a single DOF"); - } - - return _dof_name; -} - -//---------------------------------------------------------------------------// - -} // end namespace EquationSet -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EQUATIONSET_HEAT_IMPL_HPP diff --git a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.cpp b/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.cpp deleted file mode 100644 index a3d3994..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_EquationSet_IncompressibleNavierStokes.hpp" -#include "VertexCFD_EquationSet_IncompressibleNavierStokes_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL( - VertexCFD::EquationSet::IncompressibleNavierStokes) diff --git a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.hpp b/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.hpp deleted file mode 100644 index c8b92d0..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_HPP -#define VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_HPP - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace EquationSet -{ -//---------------------------------------------------------------------------// -// Isothermal Navier Stokes equations for incompressible flow. -// Navier Stokes equations for incompressible flow. -//---------------------------------------------------------------------------// -template -class IncompressibleNavierStokes - : public panzer::EquationSet_DefaultImpl -{ - public: - IncompressibleNavierStokes(const Teuchos::RCP& params, - const int& default_integration_order, - const panzer::CellData& cell_data, - const Teuchos::RCP& gd, - const bool build_transient_support); - - void buildAndRegisterEquationSetEvaluators( - PHX::FieldManager& fm, - const panzer::FieldLibrary& field_library, - const Teuchos::ParameterList& user_data) const override; - - private: - int _num_space_dim; - std::unordered_map _equ_dof_ns_pair; - std::unordered_map _equ_dof_ep_pair; - std::unordered_map _equ_dof_tm_pair; - std::unordered_map _equ_dof_fim_pair; - std::unordered_map> - _equ_source_term; - bool _build_viscous_flux; - bool _build_temp_equ; - bool _build_ind_less_equ; - bool _build_constant_source; - bool _build_buoyancy_source; - bool _build_viscous_heat; - std::string _turbulence_model; - bool _build_full_induction_model; - bool _build_resistive_flux; - bool _build_magn_corr; - bool _build_godunov_powell_source; -}; - -//---------------------------------------------------------------------------// - -} // end namespace EquationSet -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_HPP diff --git a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes_impl.hpp b/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes_impl.hpp deleted file mode 100644 index c382f2a..0000000 --- a/src/equation_sets/VertexCFD_EquationSet_IncompressibleNavierStokes_impl.hpp +++ /dev/null @@ -1,471 +0,0 @@ -#ifndef VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_IMPL_HPP -#define VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_IMPL_HPP - -#include -#include - -#include -#include - -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace EquationSet -{ -//---------------------------------------------------------------------------// -template -IncompressibleNavierStokes::IncompressibleNavierStokes( - const Teuchos::RCP& params, - const int& default_integration_order, - const panzer::CellData& cell_data, - const Teuchos::RCP& global_data, - const bool build_transient_support) - : panzer::EquationSet_DefaultImpl(params, - default_integration_order, - cell_data, - global_data, - build_transient_support) -{ - // This equation set is always transient. - if (!this->buildTransientSupport()) - { - throw std::logic_error( - "Transient support is required for solving all equation sets."); - } - - // Get the number of space dimensions. - _num_space_dim = cell_data.baseCellDimension(); - if (!(_num_space_dim == 2 || _num_space_dim == 3)) - { - throw std::runtime_error("Number of space dimensions not supported"); - } - - // Set default parameter values and validate the inputs. - Teuchos::ParameterList valid_parameters; - this->setDefaultValidParameters(valid_parameters); - valid_parameters.set( - "Model ID", "", "Closure model id associated with this equation set"); - valid_parameters.set("Basis Order", 1, "Order of the basis"); - valid_parameters.set("Integration Order", 2, "Order of the integration"); - valid_parameters.set("Build Viscous Flux", true, "Viscous flux boolean"); - valid_parameters.set( - "Build Temperature Equation", false, "Solve temperature equation"); - valid_parameters.set("Build Inductionless MHD Equation", - false, - "Solve electric potential equation"); - valid_parameters.set( - "Build Full Induction Model", false, "Solve full induction equation"); - valid_parameters.set("Build Magnetic Correction Potential Equation", - false, - "Solve full induction equation with divergence " - "cleaning"); - valid_parameters.set( - "Build Constant Source", false, "Constant source boolean"); - - valid_parameters.set( - "Build Buoyancy Source", false, "Buoyancy source boolean"); - - valid_parameters.set("Build Viscous Heat", false, "Viscous heat boolean"); - - valid_parameters.set( - "Build Resistive Flux", false, "Resistive flux boolean"); - - valid_parameters.set("Build Divergence Cleaning Source", - false, - "Divergence cleaning source boolean"); - - valid_parameters.set( - "Build Godunov-Powell Source", false, "Godunov-Powell source boolean"); - - valid_parameters.set("Build Magnetic Correction Damping Source", - false, - "Magnetic Correction damping source boolean"); - - valid_parameters.set("Build Induction Constant Source", - false, - "Induction equation constant source boolean"); - - const auto turbulence_validator = Teuchos::rcp( - new Teuchos::StringToIntegralParameterEntryValidator( - Teuchos::tuple("No Turbulence Model", - "Spalart-Allmaras", - "Standard K-Epsilon", - "Realizable K-Epsilon", - "WALE"), - "Turbulence Model")); - valid_parameters.set("Turbulence Model", - "No Turbulence Model", - "Turbulence model choice", - turbulence_validator); - - params->validateParametersAndSetDefaults(valid_parameters); - - // Extract parameters. - const int basis_order = params->get("Basis Order", 1); - const int integration_order - = params->get("Integration Order", basis_order + 1); - const std::string model_id = params->get("Model ID"); - _build_viscous_flux = params->get("Build Viscous Flux", true); - _build_temp_equ = params->get("Build Temperature Equation", false); - _build_ind_less_equ - = params->get("Build Inductionless MHD Equation", false); - _build_constant_source = params->get("Build Constant Source", false); - _build_buoyancy_source = params->get("Build Buoyancy Source", false); - _build_viscous_heat = params->get("Build Viscous Heat", false); - _turbulence_model = params->get("Turbulence Model"); - _build_full_induction_model - = params->get("Build Full Induction Model", false); - _build_magn_corr = params->get( - "Build Magnetic Correction Potential Equation", false); - _build_resistive_flux = params->get("Build Resistive Flux", false); - const bool build_div_cleaning_src - = params->get("Build Divergence Cleaning Source", false); - _build_godunov_powell_source - = params->get("Build Godunov-Powell Source", false); - const bool build_magn_corr_damping_src - = params->get("Build Magnetic Correction Damping Source"); - const bool build_ind_equ_const_source - = params->get("Build Induction Constant Source"); - - if (_build_buoyancy_source && !_build_temp_equ) - { - std::string msg = "Build Buoyancy Source set to TRUE,\n"; - msg += "but Build Temperature Equation set to FALSE.\n"; - msg += "Please enable temperature equation to solve with buoyancy source."; - throw std::runtime_error(msg); - } - - if (_build_viscous_heat && !_build_temp_equ) - { - std::string msg = "Build Viscous Heat set to TRUE.\n"; - msg += "but Build Temperature Equation set to FALSE.\n"; - msg += "Please enable temperature equation to solve with buoyancy source."; - throw std::runtime_error(msg); - } - - // Initialize equation names and variable names for NS equations - _equ_dof_ns_pair.insert({"continuity", "lagrange_pressure"}); - for (int d = 0; d < _num_space_dim; ++d) - { - const std::string ds = std::to_string(d); - _equ_dof_ns_pair.insert({"momentum_" + ds, "velocity_" + ds}); - } - if (_build_temp_equ) - _equ_dof_ns_pair.insert({"energy", "temperature"}); - - // Initialize equation name and variable name for EP equation - if (_build_ind_less_equ) - _equ_dof_ep_pair.insert( - {"electric_potential_equation", "electric_potential"}); - - // Initialize equation names and variable names for turbulence model (TM) - // equations - if (std::string::npos != _turbulence_model.find("Spalart-Allmaras")) - { - _equ_dof_tm_pair.insert( - {"spalart_allmaras_equation", "spalart_allmaras_variable"}); - } - else if (std::string::npos != _turbulence_model.find("K-Epsilon")) - { - _equ_dof_tm_pair.insert( - {"turb_kinetic_energy_equation", "turb_kinetic_energy"}); - _equ_dof_tm_pair.insert( - {"turb_dissipation_rate_equation", "turb_dissipation_rate"}); - } - - // Initialize equation names and variables names for full induction model - if (_build_full_induction_model) - { - for (int d = 0; d < _num_space_dim; ++d) - { - const std::string ds = std::to_string(d); - _equ_dof_fim_pair.insert( - {"induction_" + ds, "induced_magnetic_field_" + ds}); - } - - if (_build_magn_corr) - { - _equ_dof_fim_pair.insert({"magnetic_correction_potential", - "scalar_magnetic_potential"}); - } - } - - // Functions to set dofs - auto set_dofs - = [&, this](const std::string& equ_name, const std::string& dof_name) { - const std::string residual_name = "RESIDUAL_" + equ_name; - const std::string scatter_name = "SCATTER_" + equ_name; - const std::string basis_type = "HGrad"; - this->addDOF(dof_name, - basis_type, - basis_order, - integration_order, - residual_name, - scatter_name); - this->addDOFGrad(dof_name); - this->addDOFTimeDerivative(dof_name); - }; - - // Setup degrees of freedom for NS equations. - for (auto it : _equ_dof_ns_pair) - { - const auto equ_name = it.first; - const auto dof_name = it.second; - set_dofs(equ_name, dof_name); - }; - - // Setup degrees of freedom for EP equations. - for (auto it : _equ_dof_ep_pair) - { - const auto equ_name = it.first; - const auto dof_name = it.second; - set_dofs(equ_name, dof_name); - } - - // Setup degrees of freedom for TM equations. - for (auto it : _equ_dof_tm_pair) - { - const auto equ_name = it.first; - const auto dof_name = it.second; - set_dofs(equ_name, dof_name); - } - - // Setup degrees of freedom for FIM equations - for (auto it : _equ_dof_fim_pair) - { - const auto equ_name = it.first; - const auto dof_name = it.second; - set_dofs(equ_name, dof_name); - // Set up the source terms for induction equations - if (std::string::npos != equ_name.find("induction")) - { - std::unordered_map source_term; - source_term.insert( - {"GODUNOV_POWELL_SOURCE", _build_godunov_powell_source}); - source_term.insert({"CONSTANT_SOURCE", build_ind_equ_const_source}); - _equ_source_term.insert({equ_name, source_term}); - } - if (std::string::npos != equ_name.find("magnetic_correction")) - { - std::unordered_map source_term; - source_term.insert({"DIV_CLEANING_SOURCE", build_div_cleaning_src}); - source_term.insert({"DAMPING_SOURCE", build_magn_corr_damping_src}); - _equ_source_term.insert({equ_name, source_term}); - } - } - - // Add closure models and set-up DOFs - this->addClosureModel(model_id); - this->setupDOFs(); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleNavierStokes::buildAndRegisterEquationSetEvaluators( - PHX::FieldManager& fm, - const panzer::FieldLibrary&, - const Teuchos::ParameterList&) const -{ - // Integration data. The same rule and basis is used for all DOFs in this - // implementation so use the lagrange pressure field to get this data. - const auto it = _equ_dof_ns_pair.find("continuity"); - const auto ir = this->getIntRuleForDOF(it->second); - const auto basis = this->getBasisIRLayoutForDOF(it->second); - - // Add basis time residuals - auto add_basis_time_scalar_residual = [&, this]( - const std::string& equ_name, - const std::string& residual_name, - const double& multiplier, - std::vector& op_names) { - const std::string closure_model_residual = residual_name + "_" - + equ_name; - const std::string full_residual = "RESIDUAL_" + closure_model_residual; - auto op = Teuchos::rcp( - new panzer::Integrator_BasisTimesScalar( - panzer::EvaluatorStyle::EVALUATES, - full_residual, - closure_model_residual, - *basis, - *ir, - multiplier)); - this->template registerEvaluator(fm, op); - op_names.push_back(full_residual); - }; - - // Add grad-basis time residuals - auto add_grad_basis_time_residual = [&, this]( - const std::string& equ_name, - const std::string& residual_name, - const double& multiplier, - std::vector& op_names) { - const std::string closure_model_residual = residual_name + "_" - + equ_name; - const std::string full_residual = "RESIDUAL_" + closure_model_residual; - auto op = Teuchos::rcp( - new panzer::Integrator_GradBasisDotVector( - panzer::EvaluatorStyle::EVALUATES, - full_residual, - closure_model_residual, - *basis, - *ir, - multiplier)); - this->template registerEvaluator(fm, op); - op_names.push_back(full_residual); - }; - - // Build total residuals for NS equations - for (auto it : _equ_dof_ns_pair) - { - // Define local variables - const auto equ_name = it.first; - const auto dof_name = it.second; - std::vector residual_operator_names; - - // Time derivative residual - add_basis_time_scalar_residual( - equ_name, "DQDT", 1.0, residual_operator_names); - - // Convective flux residual - add_grad_basis_time_residual( - equ_name, "CONVECTIVE_FLUX", -1.0, residual_operator_names); - - // Viscous flux residual - if (_build_viscous_flux) - { - add_grad_basis_time_residual( - equ_name, "VISCOUS_FLUX", 1.0, residual_operator_names); - } - - // Constant source residual - if (_build_constant_source) - { - add_basis_time_scalar_residual( - equ_name, "CONSTANT_SOURCE", -1.0, residual_operator_names); - } - - // Buoyancy source residual - if (_build_buoyancy_source) - { - add_basis_time_scalar_residual( - equ_name, "BUOYANCY_SOURCE", -1.0, residual_operator_names); - } - - // Viscous heating source residual - if (_build_viscous_heat) - { - add_basis_time_scalar_residual( - equ_name, "VISCOUS_HEAT", -1.0, residual_operator_names); - } - - // Lorentz force for momentum equations - if (_build_ind_less_equ - && std::string::npos != equ_name.find("momentum")) - { - add_basis_time_scalar_residual( - equ_name, "VOLUMETRIC_SOURCE", -1.0, residual_operator_names); - } - - // Godunov-Powell source for momentum equations in full induction MHD - if (_build_full_induction_model && _build_godunov_powell_source - && std::string::npos != equ_name.find("momentum")) - { - add_basis_time_scalar_residual(equ_name, - "GODUNOV_POWELL_SOURCE", - -1.0, - residual_operator_names); - } - - // Build and register residuals - this->buildAndRegisterResidualSummationEvaluator( - fm, dof_name, residual_operator_names, "RESIDUAL_" + equ_name); - } - - // Build total residual for EP equation - for (auto it : _equ_dof_ep_pair) - { - // Define local variables - const auto equ_name = it.first; - const auto dof_name = it.second; - std::vector residual_operator_names; - - // Cross-product flux and diffusion flux residuals - add_grad_basis_time_residual( - equ_name, "ELECTRIC_POTENTIAL_FLUX", 1.0, residual_operator_names); - - // Build and register residuals - this->buildAndRegisterResidualSummationEvaluator( - fm, dof_name, residual_operator_names, "RESIDUAL_" + equ_name); - } - - // Build total residuals for TM equations - for (auto it : _equ_dof_tm_pair) - { - // Define local variables - const auto eq_name = it.first; - const auto dof_name = it.second; - std::vector residual_operator_names; - - // Add time, convective, viscous and source residuals - add_basis_time_scalar_residual( - eq_name, "DQDT", 1.0, residual_operator_names); - add_grad_basis_time_residual( - eq_name, "CONVECTIVE_FLUX", -1.0, residual_operator_names); - add_grad_basis_time_residual( - eq_name, "DIFFUSION_FLUX", 1.0, residual_operator_names); - add_basis_time_scalar_residual( - eq_name, "SOURCE", -1.0, residual_operator_names); - - // Build and register residuals - this->buildAndRegisterResidualSummationEvaluator( - fm, dof_name, residual_operator_names, "RESIDUAL_" + eq_name); - } - - // Build total residuals for FIM equations - for (auto it : _equ_dof_fim_pair) - { - // Define local variables - const auto eq_name = it.first; - const auto dof_name = it.second; - std::vector residual_operator_names; - - // Add time and convective residuals - add_basis_time_scalar_residual( - eq_name, "DQDT", 1.0, residual_operator_names); - add_grad_basis_time_residual( - eq_name, "CONVECTIVE_FLUX", -1.0, residual_operator_names); - - // Add resistive fluxes - if (_build_resistive_flux) - { - add_grad_basis_time_residual( - eq_name, "RESISTIVE_FLUX", 1.0, residual_operator_names); - } - - // Add source terms - for (const auto& [src_name, add_src] : _equ_source_term.at(eq_name)) - { - if (add_src) - { - add_basis_time_scalar_residual( - eq_name, src_name, -1.0, residual_operator_names); - } - } - - // Build and register residuals - this->buildAndRegisterResidualSummationEvaluator( - fm, dof_name, residual_operator_names, "RESIDUAL_" + eq_name); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace EquationSet -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EQUATIONSET_INCOMPRESSIBLE_NAVIERSTOKES_IMPL_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.cpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.cpp deleted file mode 100644 index 5a83502..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp" -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::FullInductionConducting) diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp deleted file mode 100644 index e5c8062..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_HPP -#define VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class FullInductionConducting : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - FullInductionConducting( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - Kokkos::Array, - num_space_dim> - _boundary_induced_magnetic_field; - PHX::MDField - _boundary_scalar_magnetic_potential; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_induced_magnetic_field; - - private: - Kokkos::Array _bnd_magn_field; - bool _build_magn_corr; - bool _build_resistive_flux; - bool _dirichlet_scalar_magn_pot; - double _bnd_scalar_magn_pot; - double _magnetic_permeability; - - PHX::MDField - _normals; - PHX::MDField - _scalar_magnetic_potential; - - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array, - num_space_dim> - _induced_magnetic_field; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_induced_magnetic_field; - - Kokkos::Array, 3> - _external_magnetic_field; - - PHX::MDField _resistivity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting_impl.hpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting_impl.hpp deleted file mode 100644 index c0c2116..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting_impl.hpp +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This function sets the boundary induced magnetic field and its gradient -// for a conducting wall with a specified boundary velocity. The calculation -// of the boundary gradient on a moving wall for resistive MHD assumes a -// constant resistivity, and may require updating for the case of variable -// resistivity. -//---------------------------------------------------------------------------// -template -FullInductionConducting::FullInductionConducting( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _boundary_scalar_magnetic_potential("BOUNDARY_scalar_magnetic_potential", - ir.dl_scalar) - , _build_magn_corr(mhd_props.buildMagnCorr()) - , _build_resistive_flux(mhd_props.buildResistiveFlux()) - , _dirichlet_scalar_magn_pot(bc_params.isType("scalar_magnetic_" - "potential")) - , _bnd_scalar_magn_pot(std::numeric_limits::signaling_NaN()) - , _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) - , _normals("Side Normal", ir.dl_vector) - , _scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) - , _resistivity("resistivity", ir.dl_scalar) -{ - for (int dim = 0; dim < num_space_dim; ++dim) - { - const std::string magn_string = "induced_magnetic_field_" - + std::to_string(dim); - _bnd_magn_field[dim] = bc_params.get(magn_string); - } - - // Add evaluated/dependent fields - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _boundary_induced_magnetic_field, - "BOUNDARY_induced_magnetic_field_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_induced_magnetic_field, - "BOUNDARY_GRAD_induced_magnetic_field_"); - - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _induced_magnetic_field, - "induced_magnetic_field_"); - - Utils::addDependentVectorField(*this, - ir.dl_vector, - _grad_induced_magnetic_field, - "GRAD_induced_magnetic_field_"); - - if (_build_magn_corr) - { - this->addEvaluatedField(_boundary_scalar_magnetic_potential); - if (_dirichlet_scalar_magn_pot) - { - _bnd_scalar_magn_pot - = bc_params.get("scalar_magnetic_potential"); - } - else - { - this->addDependentField(_scalar_magnetic_potential); - } - } - this->addDependentField(_normals); - - if (_build_resistive_flux) - { - this->addDependentField(_resistivity); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _external_magnetic_field, - "external_magnetic_field_"); - } - - this->setName("Boundary State Full Induction Conducting " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void FullInductionConducting::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void FullInductionConducting::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_induced_magnetic_field[0].extent(1); - const int num_grad_dim = _boundary_grad_induced_magnetic_field[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Compute B \cdot n - scalar_type B_dot_n = 0.0; - Kokkos::Array gradB_dot_n = {0}; - for (int grad_dim = 0; grad_dim < num_grad_dim; ++grad_dim) - { - B_dot_n += (_induced_magnetic_field[grad_dim](cell, point) - - _bnd_magn_field[grad_dim]) - * _normals(cell, point, grad_dim); - for (int field_dim = 0; field_dim < num_grad_dim; ++field_dim) - { - gradB_dot_n[field_dim] - += _grad_induced_magnetic_field[field_dim]( - cell, point, grad_dim) - * _normals(cell, point, grad_dim); - } - } - - // Compute the boundary induced magnetic field - for (int dim = 0; dim < num_space_dim; ++dim) - { - _boundary_induced_magnetic_field[dim](cell, point) - = _induced_magnetic_field[dim](cell, point); - if (dim < num_grad_dim) - { - _boundary_induced_magnetic_field[dim](cell, point) - -= B_dot_n * _normals(cell, point, dim); - } - } - - if (_build_magn_corr) - { - if (_dirichlet_scalar_magn_pot) - { - _boundary_scalar_magnetic_potential(cell, point) - = _bnd_scalar_magn_pot; - } - else - { - _boundary_scalar_magnetic_potential(cell, point) - = _scalar_magnetic_potential(cell, point); - } - } - - // include resistive contributions to boundary gradient from moving - // wall - if (_build_resistive_flux) - { - const scalar_type inv_eta = _magnetic_permeability - / _resistivity(cell, point); - for (int d = 0; d < num_grad_dim; ++d) - { - for (int fdim = 0; fdim < num_grad_dim; ++fdim) - { - if (d == fdim) - continue; - gradB_dot_n[fdim] - += _normals(cell, point, d) * inv_eta - * (_boundary_velocity[fdim](cell, point) - * (_external_magnetic_field[d](cell, point) - + _bnd_magn_field[d]) - - _boundary_velocity[d](cell, point) - * (_external_magnetic_field[fdim]( - cell, point) - + _bnd_magn_field[fdim])); - } - } - } - - // Set gradients - for (int d = 0; d < num_grad_dim; ++d) - { - for (int fdim = 0; fdim < num_space_dim; ++fdim) - { - _boundary_grad_induced_magnetic_field[fdim](cell, point, d) - = _grad_induced_magnetic_field[fdim](cell, point, d); - if (fdim < num_grad_dim) - { - _boundary_grad_induced_magnetic_field[fdim]( - cell, point, d) - -= gradB_dot_n[fdim] * _normals(cell, point, d); - } - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONCONDUCTING_IMPL_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.cpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.cpp deleted file mode 100644 index 1604bff..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp" -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::FullInductionFixed) diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp deleted file mode 100644 index 2fe3e5d..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_HPP -#define VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class FullInductionFixed : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - FullInductionFixed( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - Kokkos::Array, - num_space_dim> - _boundary_induced_magnetic_field; - PHX::MDField - _boundary_scalar_magnetic_potential; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_induced_magnetic_field; - - private: - Kokkos::Array _bnd_magn_field; - bool _build_magn_corr; - bool _dirichlet_scalar_magn_pot; - double _bnd_scalar_magn_pot; - - PHX::MDField - _scalar_magnetic_potential; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_induced_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed_impl.hpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed_impl.hpp deleted file mode 100644 index b626c65..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed_impl.hpp +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This function sets fixed boundary values for the induced magnetic field -// components from input. When solving the magnetic correction potential -// equation for divergence cleaning, the boundary value for the scalar -// magnetic potential is extrapolated (free) unless a value is set in the -// input BC parameters. -//---------------------------------------------------------------------------// -template -FullInductionFixed::FullInductionFixed( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _boundary_scalar_magnetic_potential("BOUNDARY_scalar_magnetic_potential", - ir.dl_scalar) - , _build_magn_corr(mhd_props.buildMagnCorr()) - , _dirichlet_scalar_magn_pot(bc_params.isType("scalar_magnetic_" - "potential")) - , _bnd_scalar_magn_pot(std::numeric_limits::signaling_NaN()) - , _scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) -{ - for (int dim = 0; dim < num_space_dim; ++dim) - { - const std::string magn_string = "induced_magnetic_field_" - + std::to_string(dim); - _bnd_magn_field[dim] = bc_params.get(magn_string); - } - - // Add evaluated/dependent fields - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _boundary_induced_magnetic_field, - "BOUNDARY_induced_magnetic_field_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_induced_magnetic_field, - "BOUNDARY_GRAD_induced_magnetic_field_"); - - Utils::addDependentVectorField(*this, - ir.dl_vector, - _grad_induced_magnetic_field, - "GRAD_induced_magnetic_field_"); - - if (_build_magn_corr) - { - this->addEvaluatedField(_boundary_scalar_magnetic_potential); - if (_dirichlet_scalar_magn_pot) - { - _bnd_scalar_magn_pot - = bc_params.get("scalar_magnetic_potential"); - } - else - { - this->addDependentField(_scalar_magnetic_potential); - } - } - - this->setName("Boundary State Full Induction Fixed " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void FullInductionFixed::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void FullInductionFixed::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_induced_magnetic_field[0].extent(1); - const int num_grad_dim = _boundary_grad_induced_magnetic_field[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set the fixed boundary values - for (int dim = 0; dim < num_space_dim; ++dim) - { - _boundary_induced_magnetic_field[dim](cell, point) - = _bnd_magn_field[dim]; - } - - if (_build_magn_corr) - { - if (_dirichlet_scalar_magn_pot) - { - _boundary_scalar_magnetic_potential(cell, point) - = _bnd_scalar_magn_pot; - } - else - { - _boundary_scalar_magnetic_potential(cell, point) - = _scalar_magnetic_potential(cell, point); - } - } - - // Set gradients - for (int d = 0; d < num_grad_dim; ++d) - { - for (int field_dim = 0; field_dim < num_space_dim; ++field_dim) - { - _boundary_grad_induced_magnetic_field[field_dim]( - cell, point, d) - = _grad_induced_magnetic_field[field_dim]( - cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_FULLINDUCTIONFIXED_IMPL_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.cpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.cpp deleted file mode 100644 index 264d7dd..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::FullInductionBoundaryStateFactory) diff --git a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp b/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp deleted file mode 100644 index c9372bf..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/VertexCFD_FullInductionBoundaryState_Factory.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONBOUNDARYSTATE_FACTORY_HPP -#define VERTEXCFD_FULLINDUCTIONBOUNDARYSTATE_FACTORY_HPP - -#include "closure_models/VertexCFD_Closure_ConstantScalarField.hpp" -#include "closure_models/VertexCFD_Closure_ExternalMagneticField.hpp" - -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp" -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class FullInductionBoundaryStateFactory -{ - public: - static constexpr int num_space_dim = NumSpaceDim; - - static std::vector>> - create(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const Teuchos::ParameterList& user_params, - const MHDProperties::FullInductionMHDProperties& mhd_props) - { - // Evaluator vector to return - std::vector>> evaluators; - - // Add total magnetic field, magnetic pressure, and resistivity - // closures - const auto ext_magn_field_op = Teuchos::rcp( - new ClosureModel::ExternalMagneticField( - ir, user_params)); - evaluators.push_back(ext_magn_field_op); - - const auto tot_magn_field_op = Teuchos::rcp( - new ClosureModel:: - TotalMagneticField( - ir, "BOUNDARY_")); - evaluators.push_back(tot_magn_field_op); - - const auto magn_press_op = Teuchos::rcp( - new ClosureModel::MagneticPressure( - ir, mhd_props)); - evaluators.push_back(magn_press_op); - - const auto resistivity_op = Teuchos::rcp( - new ClosureModel::ConstantScalarField( - ir, "resistivity", mhd_props.resistivity())); - evaluators.push_back(resistivity_op); - - // Loop over boundary conditions found in input file for the - // induced magnetic field and scalar magnetic potential - bool found_model = false; - if (bc_params.isType("Type")) - { - const auto bc_type = bc_params.get("Type"); - - if (bc_type == "Conducting") - { - const auto state = Teuchos::rcp( - new FullInductionConducting( - ir, bc_params, mhd_props)); - evaluators.push_back(state); - found_model = true; - } - - if (bc_type == "Fixed") - { - const auto state = Teuchos::rcp( - new FullInductionFixed( - ir, bc_params, mhd_props)); - evaluators.push_back(state); - found_model = true; - } - - // Error message if model not found - if (!found_model) - { - std::string msg = "\n\nBoundary state " + bc_type - + " failed to build.\n"; - msg += "The boundary conditions implemented in VERTEX-CFD\n"; - msg += "for the full induction equations are:\n"; - msg += "Conducting,\n"; - msg += "Fixed,\n"; - msg += "\n"; - throw std::runtime_error(msg); - } - } - - // Return vector of evaluators - return evaluators; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_FULLINDUCTIONBOUNDARYSTATE_FACTORY_HPP diff --git a/src/full_induction_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt b/src/full_induction_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index f89da12..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - FullInductionConducting - FullInductionFixed - ) diff --git a/src/full_induction_mhd_solver/boundary_conditions/unit_test/doc/fullInductionConducting_reference.py b/src/full_induction_mhd_solver/boundary_conditions/unit_test/doc/fullInductionConducting_reference.py deleted file mode 100644 index 0b68f6e..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/unit_test/doc/fullInductionConducting_reference.py +++ /dev/null @@ -1,88 +0,0 @@ -import numpy as np - - -def np_array_to_cpp_str(my_arr): - my_str = np.array2string(my_arr, separator=', ') - my_str = my_str.replace("[", "{") - my_str = my_str.replace("]", "}") - return my_str - - -def get_conducting_bc_exp_val(norm, u_bnd, b, b_bnd, b_ext, grad_b, psi, - psi_bnd, mu, eta, build_magn_corr, dir_magn_pot, - build_resistive): - b_dot_n = np.dot(b - b_bnd, norm) - grad_b_dot_n = [] - for i in range(len(grad_b)): - grad_b_dot_n.append(np.dot(grad_b[i], norm)) - - exp_b = b - for i in range(len(b)): - if (i < len(norm)): - exp_b[i] -= b_dot_n * norm[i] - - if build_resistive: - # i-loop over the magnetic field dimensions - for i in range(len(grad_b)): - # j-loop over the gradient/spatial dimensions - for j in range(len(grad_b[i])): - if i != j: - grad_b_dot_n[i] += mu / eta * norm[j] * ( - u_bnd[i] * (b_ext[j] + b_bnd[j]) - u_bnd[j] * - (b_ext[i] + b_bnd[i])) - - exp_grad_b = grad_b - for i in range(len(grad_b)): - for j in range(len(grad_b[i])): - if (j < len(norm)): - exp_grad_b[i][j] -= grad_b_dot_n[i] * norm[j] - - print("exp_b =\n", np_array_to_cpp_str(exp_b), ";\n") - print("exp_grad_b =\n", np_array_to_cpp_str(exp_grad_b), ";\n") - if build_magn_corr: - exp_psi = psi - if dir_magn_pot: - exp_psi = psi_bnd - print("exp_psi =", exp_psi, ";\n") - - -def get_case_exp_vals(n_grad, n_space, build_magn_corr, dir_magn_pot, - build_resistive): - u_bnd = np.array([3.0, -4.0, 5.0]) - b = np.array([1.25, 2.5, 3.75]) - b_bnd = np.array([1.1, 2.2, 3.3]) - b_ext = np.array([-0.05, 0.025, -0.0125]) - norm = np.array([.45, -.65, .35]) - grad_b = np.array([[11.0, -5.5, 2.75], [-6.0, 3.0, -1.5], - [3.25, -1.625, 0.8125]]) - psi = 4.4 - psi_bnd = 5.5 - mu = 0.12 - eta = 3.6 - - print("\nFull Induction Conducting Wall Case:\n") - print("\tnum_grad_dim = ", n_grad) - print("\tnum_space_dim = ", n_space) - print("\tbuild_magn_corr = ", build_magn_corr) - print("\tdirichlet_scalar_magn_pot = ", dir_magn_pot) - print("\tbuild_resistive_flux = ", build_resistive, "\n") - - get_conducting_bc_exp_val(norm[:n_grad], u_bnd[:n_space], b[:n_space], - b_bnd[:n_space], b_ext[:n_space], - grad_b[:n_space, :n_grad], psi, psi_bnd, mu, eta, - build_magn_corr, dir_magn_pot, build_resistive) - - -get_case_exp_vals(2, 2, False, False, False) -get_case_exp_vals(2, 2, False, False, True) -get_case_exp_vals(2, 2, True, False, False) -get_case_exp_vals(2, 2, True, False, True) -get_case_exp_vals(2, 2, True, True, False) -get_case_exp_vals(2, 2, True, True, True) - -get_case_exp_vals(3, 3, False, False, False) -get_case_exp_vals(3, 3, False, False, True) -get_case_exp_vals(3, 3, True, False, False) -get_case_exp_vals(3, 3, True, False, True) -get_case_exp_vals(3, 3, True, True, False) -get_case_exp_vals(3, 3, True, True, True) diff --git a/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionConducting.cpp b/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionConducting.cpp deleted file mode 100644 index a2e79cc..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionConducting.cpp +++ /dev/null @@ -1,383 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionConducting.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - static constexpr int num_field_dim = 3; - int num_space_dim; - - double _scalar_magn_pot; - double _eta; - - PHX::MDField _boundary_velocity_0; - PHX::MDField _boundary_velocity_1; - PHX::MDField _boundary_velocity_2; - - Kokkos::Array, - num_field_dim> - _induced_magnetic_field; - - Kokkos::Array< - PHX::MDField, - num_field_dim> - _grad_induced_magnetic_field; - - PHX::MDField - _scalar_magnetic_potential; - - PHX::MDField _resistivity; - - Kokkos::Array, - num_field_dim> - _external_magnetic_field; - - PHX::MDField _normals; - - Dependencies(const panzer::IntegrationRule& ir, - const double scalar_magn_pot, - const double eta) - : num_space_dim(ir.spatial_dimension) - , _scalar_magn_pot(scalar_magn_pot) - , _eta(eta) - , _boundary_velocity_0("BOUNDARY_velocity_0", ir.dl_scalar) - , _boundary_velocity_1("BOUNDARY_velocity_1", ir.dl_scalar) - , _boundary_velocity_2("BOUNDARY_velocity_2", ir.dl_scalar) - , _scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) - , _resistivity("resistivity", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(_boundary_velocity_0); - this->addEvaluatedField(_boundary_velocity_1); - if (num_space_dim == 3) - this->addEvaluatedField(_boundary_velocity_2); - - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _induced_magnetic_field, - "induced_magnetic_field_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _grad_induced_magnetic_field, - "GRAD_induced_magnetic_field_"); - - this->addEvaluatedField(_scalar_magnetic_potential); - this->addEvaluatedField(_resistivity); - - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _external_magnetic_field, - "external_magnetic_field_"); - - this->addEvaluatedField(_normals); - - this->setName( - "Full Induction Model Conducting Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - _boundary_velocity_0.deep_copy(3.0); - _boundary_velocity_1.deep_copy(-4.0); - if (num_space_dim == 3) - _boundary_velocity_2.deep_copy(5.0); - - for (int dim = 0; dim < num_field_dim; ++dim) - { - _induced_magnetic_field[dim].deep_copy(1.25 * (dim + 1)); - _external_magnetic_field[dim].deep_copy(pow(-0.5, dim + 1) * 0.1); - } - - _scalar_magnetic_potential.deep_copy(_scalar_magn_pot); - _resistivity.deep_copy(_eta); - - Kokkos::parallel_for( - "full induction model conducting unit test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _grad_induced_magnetic_field[0].extent(1); - const int num_grad_dim = _grad_induced_magnetic_field[0].extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - _normals(c, qp, 0) = 0.45; - _normals(c, qp, 1) = -0.65; - if (num_grad_dim == 3) - _normals(c, qp, 2) = 0.35; - for (int fdim = 0; fdim < num_field_dim; ++fdim) - { - for (int gdim = 0; gdim < num_grad_dim; ++gdim) - { - _grad_induced_magnetic_field[fdim](c, qp, gdim) - = (fdim + 11) * pow(-0.5, fdim + gdim); - } - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(bool build_magn_corr, - bool dirichlet_scalar_magn_pot, - bool build_resistive_flux) -{ - // Test fixture - static constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const double nanval = std::numeric_limits::signaling_NaN(); - const double scalar_magn_pot - = (build_magn_corr && !dirichlet_scalar_magn_pot) ? 4.4 : nanval; - const Kokkos::Array bnd_b = {1.1, 2.2, 3.3}; - const double eta = 3.6; - - // Create dependencies - const auto dep_eval = Teuchos::rcp( - new Dependencies(*test_fixture.ir, scalar_magn_pot, eta)); - test_fixture.registerEvaluator(dep_eval); - - // Create conducting wall evaluator. - const double bnd_scalar_magn_pot - = (build_magn_corr && dirichlet_scalar_magn_pot) ? 5.5 : nanval; - - Teuchos::ParameterList bc_params; - bc_params.set("induced_magnetic_field_0", bnd_b[0]); - bc_params.set("induced_magnetic_field_1", bnd_b[1]); - if (num_space_dim == 3) - bc_params.set("induced_magnetic_field_2", bnd_b[2]); - if (build_magn_corr && dirichlet_scalar_magn_pot) - bc_params.set("scalar_magnetic_potential", bnd_scalar_magn_pot); - - Teuchos::ParameterList full_indu_params; - full_indu_params.set("Vacuum Magnetic Permeability", 0.12); - full_indu_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - full_indu_params.set("Hyperbolic Divergence Cleaning Speed", 1.1); - full_indu_params.set("Build Resistive Flux", build_resistive_flux); - full_indu_params.set("Resistivity", eta); - MHDProperties::FullInductionMHDProperties mhd_props(full_indu_params); - - const auto cond_eval = Teuchos::rcp( - new BoundaryCondition:: - FullInductionConducting( - *test_fixture.ir, bc_params, mhd_props)); - - test_fixture.registerEvaluator(cond_eval); - - // Add required test fields. - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - cond_eval->_boundary_induced_magnetic_field[dim]); - test_fixture.registerTestField( - cond_eval->_boundary_grad_induced_magnetic_field[dim]); - } - if (build_magn_corr) - { - test_fixture.registerTestField( - cond_eval->_boundary_scalar_magnetic_potential); - } - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto bnd_magn_field_0_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_induced_magnetic_field[0]); - const auto bnd_grad_magn_field_0_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_grad_induced_magnetic_field[0]); - const auto bnd_magn_field_1_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_induced_magnetic_field[1]); - const auto bnd_grad_magn_field_1_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_grad_induced_magnetic_field[1]); - - // Set up expected values based on case - // For now VERTEX only supports num_grad_dim = num_space_dim, - // but the reference script can produce values for the case - // where num_grad_dim = 2 and num_space_dim = 3 - const Kokkos::Array exp_b_2d = {1.307375, 2.417125, nanval}; - const Kokkos::Array exp_b_3d = {1.2365, 2.5195, 3.7395}; - - const auto exp_b = num_space_dim == 2 ? exp_b_2d : exp_b_3d; - - const Kokkos::Array, 3> exp_grad_b_2d - = {{{7.16375, 0.04125, nanval}, - {-3.9075, -0.0225, nanval}, - {nanval, nanval, nanval}}}; - const Kokkos::Array, 3> exp_grad_b_2d_res - = {{{7.26978125, -0.11190625, nanval}, - {-3.83409375, -0.12853125, nanval}, - {nanval, nanval, nanval}}}; - const Kokkos::Array, 3> exp_grad_b_3d - = {{{6.730625, 0.666875, -0.570625}, - {-3.67125, -0.36375, 0.31125}, - {1.98859375, 0.19703125, -0.16859375}}}; - const Kokkos::Array, 3> exp_grad_b_3d_res - = {{{6.81244062, 0.54869688, -0.50699062}, - {-3.4704, -0.65386667, 0.46746667}, - {2.25640937, -0.18981354, 0.03970729}}}; - - const auto exp_grad_b = num_space_dim == 2 ? build_resistive_flux - ? exp_grad_b_2d_res - : exp_grad_b_2d - : build_resistive_flux ? exp_grad_b_3d_res - : exp_grad_b_3d; - - const double exp_scalar_magn_pot - = dirichlet_scalar_magn_pot ? bnd_scalar_magn_pot : scalar_magn_pot; - - const auto& ir = *test_fixture.ir; - const int num_point = ir.num_points; - const double tol = build_resistive_flux ? 6.0e-9 : 1.0e-12; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_b[0], fieldValue(bnd_magn_field_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(exp_b[1], fieldValue(bnd_magn_field_1_result, 0, qp)); - if (num_space_dim == 3) - { - const auto bnd_magn_field_2_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_induced_magnetic_field[2]); - EXPECT_DOUBLE_EQ(exp_b[2], - fieldValue(bnd_magn_field_2_result, 0, qp)); - } - if (build_magn_corr) - { - const auto bnd_scalar_magn_pot_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_scalar_magnetic_potential); - EXPECT_DOUBLE_EQ(exp_scalar_magn_pot, - fieldValue(bnd_scalar_magn_pot_result, 0, qp)); - } - - for (int d = 0; d < num_space_dim; ++d) - { - EXPECT_NEAR(exp_grad_b[0][d], - fieldValue(bnd_grad_magn_field_0_result, 0, qp, d), - tol); - EXPECT_NEAR(exp_grad_b[1][d], - fieldValue(bnd_grad_magn_field_1_result, 0, qp, d), - tol); - if (num_space_dim == 3) - { - const auto bnd_grad_magn_field_2_result - = test_fixture.getTestFieldData( - cond_eval->_boundary_grad_induced_magnetic_field[2]); - EXPECT_NEAR(exp_grad_b[2][d], - fieldValue(bnd_grad_magn_field_2_result, 0, qp, d), - tol); - } - } - } -} - -//---------------------------------------------------------------------------// -struct FullInductionConductingTestParams -{ - bool build_magn_corr; - bool dirichlet_scalar_magn_pot; - bool build_resistive_flux; -}; - -class FullInductionConducting - : public testing::TestWithParam -{ - public: - struct PrintNameString - { - template - std::string operator()(const testing::TestParamInfo& info) const - { - auto p = static_cast(info.param); - - const std::string base_name = p.build_resistive_flux ? "Resistive" - : ""; - const std::string pot_name = !p.build_magn_corr ? "NoCleaning" - : p.dirichlet_scalar_magn_pot - ? "FixedScalarMagneticPotential" - : "FreeScalarMagneticPotential"; - return base_name + pot_name; - } - }; -}; - -//---------------------------------------------------------------------------// -TEST_P(FullInductionConducting, Residual2d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot, - params.build_resistive_flux); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionConducting, Jacobian2d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot, - params.build_resistive_flux); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionConducting, Residual3d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot, - params.build_resistive_flux); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionConducting, Jacobian3d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot, - params.build_resistive_flux); -} - -//---------------------------------------------------------------------------// -INSTANTIATE_TEST_SUITE_P( - Test, - FullInductionConducting, - testing::Values(FullInductionConductingTestParams{false, false, false}, - FullInductionConductingTestParams{false, false, true}, - FullInductionConductingTestParams{true, false, false}, - FullInductionConductingTestParams{true, false, true}, - FullInductionConductingTestParams{true, true, false}, - FullInductionConductingTestParams{true, true, true}), - FullInductionConducting::PrintNameString()); - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionFixed.cpp b/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionFixed.cpp deleted file mode 100644 index f42eb4c..0000000 --- a/src/full_induction_mhd_solver/boundary_conditions/unit_test/tstFullInductionFixed.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "full_induction_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_FullInductionFixed.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - static constexpr int num_field_dim = 3; - - Kokkos::Array _grad_b; - double _scalar_magn_pot; - - Kokkos::Array< - PHX::MDField, - 3> - _grad_induced_magnetic_field; - PHX::MDField - _scalar_magnetic_potential; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array grad_b, - const double scalar_magn_pot) - : _grad_b(grad_b) - , _scalar_magn_pot(scalar_magn_pot) - , _scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) - { - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _grad_induced_magnetic_field, - "GRAD_induced_magnetic_field_"); - - this->addEvaluatedField(_scalar_magnetic_potential); - - this->setName("Full Induction Model Fixed Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - for (int dim = 0; dim < num_field_dim; ++dim) - { - _grad_induced_magnetic_field[dim].deep_copy(_grad_b[dim]); - } - _scalar_magnetic_potential.deep_copy(_scalar_magn_pot); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(bool build_magn_corr, bool dirichlet_scalar_magn_pot) -{ - // Test fixture - static constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const double nanval = std::numeric_limits::signaling_NaN(); - const Kokkos::Array grad_b - = {1.1, 2.2, num_space_dim == 2 ? nanval : 3.3}; - const double scalar_magn_pot - = (build_magn_corr && !dirichlet_scalar_magn_pot) ? 4.4 : nanval; - - // Create dependencies - const auto dep_eval = Teuchos::rcp( - new Dependencies(*test_fixture.ir, grad_b, scalar_magn_pot)); - test_fixture.registerEvaluator(dep_eval); - - // Create fixed evaluator. - const Kokkos::Array bnd_b - = {0.3, 0.4, num_space_dim == 2 ? nanval : 0.5}; - const double bnd_scalar_magn_pot - = (build_magn_corr && dirichlet_scalar_magn_pot) ? 5.5 : nanval; - const double exp_scalar_magn_pot - = dirichlet_scalar_magn_pot ? bnd_scalar_magn_pot : scalar_magn_pot; - - Teuchos::ParameterList bc_params; - bc_params.set("induced_magnetic_field_0", bnd_b[0]); - bc_params.set("induced_magnetic_field_1", bnd_b[1]); - if (num_space_dim == 3) - bc_params.set("induced_magnetic_field_2", bnd_b[2]); - if (build_magn_corr && dirichlet_scalar_magn_pot) - bc_params.set("scalar_magnetic_potential", bnd_scalar_magn_pot); - - Teuchos::ParameterList full_indu_params; - full_indu_params.set("Vacuum Magnetic Permeability", 0.1); - full_indu_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - full_indu_params.set("Hyperbolic Divergence Cleaning Speed", 1.1); - MHDProperties::FullInductionMHDProperties mhd_props(full_indu_params); - - const auto fixed_eval = Teuchos::rcp( - new BoundaryCondition:: - FullInductionFixed( - *test_fixture.ir, bc_params, mhd_props)); - - test_fixture.registerEvaluator(fixed_eval); - - // Add required test fields. - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - fixed_eval->_boundary_induced_magnetic_field[dim]); - test_fixture.registerTestField( - fixed_eval->_boundary_grad_induced_magnetic_field[dim]); - } - if (build_magn_corr) - { - test_fixture.registerTestField( - fixed_eval->_boundary_scalar_magnetic_potential); - } - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto bnd_magn_field_0_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_induced_magnetic_field[0]); - const auto bnd_grad_magn_field_0_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_induced_magnetic_field[0]); - const auto bnd_magn_field_1_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_induced_magnetic_field[1]); - const auto bnd_grad_magn_field_1_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_induced_magnetic_field[1]); - - const int num_point = bnd_magn_field_0_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(bnd_b[0], fieldValue(bnd_magn_field_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(bnd_b[1], fieldValue(bnd_magn_field_1_result, 0, qp)); - if (num_space_dim == 3) - { - const auto bnd_magn_field_2_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_induced_magnetic_field[2]); - EXPECT_DOUBLE_EQ(bnd_b[2], - fieldValue(bnd_magn_field_2_result, 0, qp)); - } - if (build_magn_corr) - { - const auto bnd_scalar_magn_pot_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_scalar_magnetic_potential); - EXPECT_DOUBLE_EQ(exp_scalar_magn_pot, - fieldValue(bnd_scalar_magn_pot_result, 0, qp)); - } - - for (int d = 0; d < num_space_dim; ++d) - { - EXPECT_DOUBLE_EQ( - grad_b[0], fieldValue(bnd_grad_magn_field_0_result, 0, qp, d)); - EXPECT_DOUBLE_EQ( - grad_b[1], fieldValue(bnd_grad_magn_field_1_result, 0, qp, d)); - if (num_space_dim == 3) - { - const auto bnd_grad_magn_field_2_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_induced_magnetic_field[2]); - EXPECT_DOUBLE_EQ( - grad_b[2], - fieldValue(bnd_grad_magn_field_2_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -struct FullInductionFixedTestParams -{ - bool build_magn_corr; - bool dirichlet_scalar_magn_pot; -}; - -class FullInductionFixed - : public testing::TestWithParam -{ - public: - struct PrintNameString - { - template - std::string operator()(const testing::TestParamInfo& info) const - { - auto p = static_cast(info.param); - const std::string test_name = !p.build_magn_corr ? "NoCleaning" - : p.dirichlet_scalar_magn_pot - ? "FixedScalarMagneticPotential" - : "FreeScalarMagneticPotential"; - return test_name; - } - }; -}; - -//---------------------------------------------------------------------------// -TEST_P(FullInductionFixed, residual_2d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionFixed, jacobian_2d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionFixed, residual_3d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot); -} - -//---------------------------------------------------------------------------// -TEST_P(FullInductionFixed, jacobian_3d) -{ - auto params = GetParam(); - testEval(params.build_magn_corr, - params.dirichlet_scalar_magn_pot); -} - -//---------------------------------------------------------------------------// -INSTANTIATE_TEST_SUITE_P( - Test, - FullInductionFixed, - testing::Values(FullInductionFixedTestParams{false, false}, - FullInductionFixedTestParams{true, false}, - FullInductionFixedTestParams{true, true}), - FullInductionFixed::PrintNameString()); - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.cpp deleted file mode 100644 index e18647c..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_DivergenceCleaningSource.hpp" -#include "VertexCFD_Closure_DivergenceCleaningSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::DivergenceCleaningSource) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp deleted file mode 100644 index 39dde03..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_HPP -#define VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Hyperbolic divergence cleaning source term for the magnetic correction -// potential equation -//---------------------------------------------------------------------------// -template -class DivergenceCleaningSource : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - DivergenceCleaningSource(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _div_cleaning_potential_source; - - private: - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField - _grad_scalar_magnetic_potential; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource_impl.hpp deleted file mode 100644 index 12c14f4..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource_impl.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -DivergenceCleaningSource::DivergenceCleaningSource( - const panzer::IntegrationRule& ir) - : _div_cleaning_potential_source( - "DIV_CLEANING_SOURCE_magnetic_correction_potential", ir.dl_scalar) - , _grad_scalar_magnetic_potential("GRAD_scalar_magnetic_potential", - ir.dl_vector) -{ - // Evaluated fields - this->addEvaluatedField(_div_cleaning_potential_source); - - // Dependent fields - this->addDependentField(_grad_scalar_magnetic_potential); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - - this->setName("Divergence Cleaning Source " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void DivergenceCleaningSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void DivergenceCleaningSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_scalar_magnetic_potential.extent(1); - const int num_grad_dim = _grad_scalar_magnetic_potential.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _div_cleaning_potential_source(cell, point) = 0.0; - - for (int dim = 0; dim < num_grad_dim; ++dim) - { - _div_cleaning_potential_source(cell, point) - -= _velocity[dim](cell, point) - * _grad_scalar_magnetic_potential(cell, point, dim); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_DIVERGENCECLEANINGSOURCE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.cpp deleted file mode 100644 index 6cdcbdc..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp" -#include "VertexCFD_Closure_FullInductionLocalTimeStepSize_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::FullInductionLocalTimeStepSize) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp deleted file mode 100644 index 2dca978..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_HPP -#define VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute local time step size based on the mesh size and maximum eigenvalue -// for the full induction MHD equation set (under incompressible assumption). -// The time step size used by the solver and based on the CFL condition is -// computed in the observer -// 'VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp' -//---------------------------------------------------------------------------// -template -class FullInductionLocalTimeStepSize - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - FullInductionLocalTimeStepSize( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData d) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _local_dt; - - private: - double _rho; - double _magnetic_permeability; - double _c_h; - - PHX::MDField - _element_length; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, 3> - _total_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize_impl.hpp deleted file mode 100644 index eb65914..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize_impl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_IMPL_HPP -#define VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_IMPL_HPP - -#include - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -FullInductionLocalTimeStepSize:: - FullInductionLocalTimeStepSize( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _local_dt("local_dt", ir.dl_scalar) - , _rho(fluid_prop.constantDensity()) - , _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) - , _c_h(mhd_props.hyperbolicDivergenceCleaningSpeed()) - , _element_length("element_length", ir.dl_vector) -{ - // Add evaluated field - this->addEvaluatedField(_local_dt); - - // Add dependent fields - this->addDependentField(_element_length); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - - this->setName("Full Induction Local Time Step Size"); -} - -//---------------------------------------------------------------------------// -template -void FullInductionLocalTimeStepSize::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void FullInductionLocalTimeStepSize::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _local_dt.extent(1); - const int num_grad_dim = _element_length.extent(2); - - const double sqrt_mu_0_rho_inv - = 1.0 / std::sqrt(_magnetic_permeability * _rho); - - const double tol = 1.0e-8; - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _local_dt(cell, point) = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - using SmoothMath::abs; - using SmoothMath::max; - _local_dt(cell, point) - += (abs(_velocity[dim](cell, point), tol) - + max(abs(_total_magnetic_field[dim](cell, point), tol) - * sqrt_mu_0_rho_inv, - _c_h, - tol)) - / _element_length(cell, point, dim); - } - _local_dt(cell, point) = 1.0 / _local_dt(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_FULLINDUCTIONLOCALTIMESTEPSIZE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp deleted file mode 100644 index 4340066..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_HPP -#define VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute error norms between exact solution and numerical solutions -//---------------------------------------------------------------------------// -template -class FullInductionModelErrorNorms - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - FullInductionModelErrorNorms(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _L1_error_induced; - - Kokkos::Array, - num_space_dim> - _L2_error_induced; - - PHX::MDField _volume; - - private: - Kokkos::Array, - num_space_dim> - _exact_induced_magnetic_field; - - Kokkos::Array, - num_space_dim> - _induced_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // namespace ClosureModel -} // end namespace VertexCFD - -#include "VertexCFD_Closure_FullInductionModelErrorNorms_impl.hpp" - -#endif // VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms_impl.hpp deleted file mode 100644 index e2f0f83..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms_impl.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_IMPL_HPP -#define VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -FullInductionModelErrorNorms:: - FullInductionModelErrorNorms(const panzer::IntegrationRule& ir) - : _volume("volume", ir.dl_scalar) -{ - // exact solution - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _exact_induced_magnetic_field, - "Exact_induced_magnetic_field_"); - - // numerical solution - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _induced_magnetic_field, - "induced_magnetic_field_"); - - // error between exact and numerical solution - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _L1_error_induced, "L1_Error_induction_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _L2_error_induced, "L2_Error_induction_"); - - this->addEvaluatedField(_volume); - - this->setName("Full Induction Model Error Norms " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void FullInductionModelErrorNorms::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void FullInductionModelErrorNorms::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _induced_magnetic_field[0].extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::abs; - using std::pow; - - // L1/L2 error norms - for (int i = 0; i < num_space_dim; ++i) - { - _L1_error_induced[i](cell, point) - = abs(_induced_magnetic_field[i](cell, point) - - _exact_induced_magnetic_field[i](cell, point)); - _L2_error_induced[i](cell, point) - = pow(_induced_magnetic_field[i](cell, point) - - _exact_induced_magnetic_field[i](cell, point), - 2); - } - - _volume(cell, point) = 1.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_FULLINDUCTIONMODELERRORNORMS_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.cpp deleted file mode 100644 index 50f0faa..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_GodunovPowellSource.hpp" -#include "VertexCFD_Closure_GodunovPowellSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::GodunovPowellSource) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp deleted file mode 100644 index c1285cd..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_HPP -#define VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Godunov-Powell source evaluation for full induction MHD -//---------------------------------------------------------------------------// -template -class GodunovPowellSource : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - GodunovPowellSource( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _godunov_powell_momentum_source; - - Kokkos::Array, - num_space_dim> - _godunov_powell_induction_source; - - private: - double _magnetic_permeability; - - PHX::MDField - _divergence_total_magnetic_field; - - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, 3> - _total_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource_impl.hpp deleted file mode 100644 index 1c6e1d9..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource_impl.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -GodunovPowellSource::GodunovPowellSource( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) - , _divergence_total_magnetic_field("divergence_total_magnetic_field", - ir.dl_scalar) -{ - // Evaluated fields - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _godunov_powell_momentum_source, - "GODUNOV_POWELL_SOURCE_momentum_"); - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _godunov_powell_induction_source, - "GODUNOV_POWELL_SOURCE_induction_"); - - // Dependent fields - this->addDependentField(_divergence_total_magnetic_field); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - - this->setName("Godunov-Powell Source " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void GodunovPowellSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void GodunovPowellSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _divergence_total_magnetic_field.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - _godunov_powell_momentum_source[dim](cell, point) - = -_divergence_total_magnetic_field(cell, point) - * _total_magnetic_field[dim](cell, point) - / _magnetic_permeability; - _godunov_powell_induction_source[dim](cell, point) - = -_divergence_total_magnetic_field(cell, point) - * _velocity[dim](cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_GODUNOVPOWELLSOURCE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.cpp deleted file mode 100644 index 85d3fee..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_InductionConstantSource.hpp" -#include "VertexCFD_Closure_InductionConstantSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::InductionConstantSource) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp deleted file mode 100644 index 69a7464..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Linear-in-time external magnetic field source for induction equations -//---------------------------------------------------------------------------// -template -class InductionConstantSource : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - InductionConstantSource(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& model_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _induction_source; - - private: - Kokkos::Array _ind_input_source; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource_impl.hpp deleted file mode 100644 index 15ccf82..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource_impl.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -InductionConstantSource::InductionConstantSource( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params) -{ - const auto ind_input_source - = closure_params.get>("Induction Source"); - - for (int dim = 0; dim < num_space_dim; ++dim) - _ind_input_source[dim] = ind_input_source[dim]; - - // Evaluated fields - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _induction_source, "CONSTANT_SOURCE_induction_"); - - this->setName("Induction Constant Source " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void InductionConstantSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void InductionConstantSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _induction_source[0].extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - _induction_source[dim](cell, point) = _ind_input_source[dim]; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONCONSTANTSOURCE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.cpp deleted file mode 100644 index 424f7a2..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_InductionConvectiveFlux.hpp" -#include "VertexCFD_Closure_InductionConvectiveFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::InductionConvectiveFlux) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp deleted file mode 100644 index 5f0c79a..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension induction convective flux evaluation. -//---------------------------------------------------------------------------// -template -class InductionConvectiveFlux : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - InductionConvectiveFlux( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props, - const std::string& flux_prefix = "", - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _momentum_flux; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _induction_flux; - PHX::MDField - _magnetic_correction_potential_flux; - - private: - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, 3> - _total_magnetic_field; - PHX::MDField - _scalar_magnetic_potential; - PHX::MDField _magnetic_pressure; - - bool _solve_magn_corr; - double _magnetic_permeability; - double _c_h; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux_impl.hpp deleted file mode 100644 index 316c600..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux_impl.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -InductionConvectiveFlux::InductionConvectiveFlux( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props, - const std::string& flux_prefix, - const std::string& field_prefix) - : _magnetic_correction_potential_flux( - flux_prefix + "CONVECTIVE_FLUX_magnetic_correction_potential", - ir.dl_vector) - , _scalar_magnetic_potential(field_prefix + "scalar_magnetic_potential", - ir.dl_scalar) - , _magnetic_pressure("magnetic_pressure", ir.dl_scalar) - , _solve_magn_corr(mhd_props.buildMagnCorr()) - , _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) - , _c_h(mhd_props.hyperbolicDivergenceCleaningSpeed()) -{ - // Contributed fields - Utils::addContributedVectorField(*this, ir.dl_vector, _momentum_flux, - flux_prefix + "CONVECTIVE_FLUX_" - "momentum_"); - - // Evaluated fields - this->addEvaluatedField(_magnetic_correction_potential_flux); - - Utils::addEvaluatedVectorField(*this, ir.dl_vector, _induction_flux, - flux_prefix + "CONVECTIVE_FLUX_" - "induction_"); - - if (_solve_magn_corr) - { - this->addEvaluatedField(_magnetic_correction_potential_flux); - } - - // Dependent fields - Utils::addDependentVectorField( - *this, ir.dl_scalar, _velocity, field_prefix + "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - this->addDependentField(_magnetic_pressure); - - if (_solve_magn_corr) - { - this->addDependentField(_scalar_magnetic_potential); - } - - this->setName("Induction Convective Flux " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void InductionConvectiveFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void InductionConvectiveFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _induction_flux[0].extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int flux_dim = 0; flux_dim < num_space_dim; ++flux_dim) - { - for (int vec_dim = 0; vec_dim < num_space_dim; ++vec_dim) - { - _momentum_flux[vec_dim](cell, point, flux_dim) - -= _total_magnetic_field[flux_dim](cell, point) - * _total_magnetic_field[vec_dim](cell, point) - / _magnetic_permeability; - if (vec_dim != flux_dim) - { - // Set the off-diagonal flux terms for the induction - // equation. - _induction_flux[vec_dim](cell, point, flux_dim) - = _velocity[flux_dim](cell, point) - * _total_magnetic_field[vec_dim](cell, point) - - _total_magnetic_field[flux_dim](cell, point) - * _velocity[vec_dim](cell, point); - } - } - // Add the magnetic pressure contribution to momentum flux. - _momentum_flux[flux_dim](cell, point, flux_dim) - += _magnetic_pressure(cell, point); - // Set diagonal flux terms for the induction equation, which - // are nonzero only when using divergence cleaning. - if (_solve_magn_corr) - { - _induction_flux[flux_dim](cell, point, flux_dim) - = _c_h * _scalar_magnetic_potential(cell, point); - _magnetic_correction_potential_flux(cell, point, flux_dim) - = _c_h * _total_magnetic_field[flux_dim](cell, point); - } - else - { - _induction_flux[flux_dim](cell, point, flux_dim) = 0.0; - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONCONVECTIVEFLUX_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.cpp deleted file mode 100644 index 02a51d0..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_InductionResistiveFlux.hpp" -#include "VertexCFD_Closure_InductionResistiveFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::InductionResistiveFlux) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp deleted file mode 100644 index ddcdc6a..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension induction resistive flux evaluation. -//---------------------------------------------------------------------------// -template -class InductionResistiveFlux : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - InductionResistiveFlux( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props, - const std::string& flux_prefix = "", - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _induction_flux; - PHX::MDField - _magnetic_correction_potential_flux; - - private: - Kokkos::Array, 3> - _total_magnetic_field; - PHX::MDField _resistivity; - - Kokkos::Array< - PHX::MDField, - 3> - _grad_total_magnetic_field; - PHX::MDField - _grad_resistivity; - - bool _variable_resistivity; - bool _solve_magn_corr; - double _magnetic_permeability; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux_impl.hpp deleted file mode 100644 index 228a6ea..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux_impl.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -InductionResistiveFlux::InductionResistiveFlux( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props, - const std::string& flux_prefix, - const std::string& gradient_prefix) - : _magnetic_correction_potential_flux( - flux_prefix + "RESISTIVE_FLUX_magnetic_correction_potential", - ir.dl_vector) - , _resistivity("resistivity", ir.dl_scalar) - , _grad_resistivity("GRAD_resistivity", ir.dl_vector) - , _variable_resistivity(mhd_props.variableResistivity()) - , _solve_magn_corr(mhd_props.buildMagnCorr()) - , _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) -{ - // Evaluated fields - this->addEvaluatedField(_magnetic_correction_potential_flux); - - Utils::addEvaluatedVectorField(*this, ir.dl_vector, _induction_flux, - flux_prefix + "RESISTIVE_FLUX_" - "induction_"); - - if (_solve_magn_corr) - { - this->addEvaluatedField(_magnetic_correction_potential_flux); - } - - // Dependent fields - this->addDependentField(_resistivity); - if (_variable_resistivity) - { - this->addDependentField(_grad_resistivity); - } - - Utils::addDependentVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - Utils::addDependentVectorField( - *this, - ir.dl_vector, - _grad_total_magnetic_field, - gradient_prefix + "GRAD_total_magnetic_field_"); - - this->setName("Induction Resistive Flux " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void InductionResistiveFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void InductionResistiveFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _induction_flux[0].extent(1); - const int num_grad_dim = _induction_flux[0].extent(2); - const double mu_0_inv = 1.0 / _magnetic_permeability; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // for constant resistivity Div(eta B) = eta*Div(B) - scalar_type div_eta_b = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - div_eta_b += _grad_total_magnetic_field[dim](cell, point, dim); - } - div_eta_b *= _resistivity(cell, point); - - // eta * Grad(B) contribution - for (int flux_dim = 0; flux_dim < num_grad_dim; ++flux_dim) - { - for (int vec_dim = 0; vec_dim < num_space_dim; ++vec_dim) - { - _induction_flux[vec_dim](cell, point, flux_dim) - = _resistivity(cell, point) - * _grad_total_magnetic_field[vec_dim]( - cell, point, flux_dim); - } - } - - if (_variable_resistivity) - { - // Div(eta B) = eta*Div(B) + grad(eta).B - for (int dim = 0; dim < num_grad_dim; ++dim) - { - div_eta_b += _grad_resistivity(cell, point, dim) - * _total_magnetic_field[dim](cell, point); - } - - // B \otimes grad(eta) contribution - for (int flux_dim = 0; flux_dim < num_grad_dim; ++flux_dim) - { - for (int vec_dim = 0; vec_dim < num_grad_dim; ++vec_dim) - { - _induction_flux[vec_dim](cell, point, flux_dim) - += _total_magnetic_field[flux_dim](cell, point) - * _grad_resistivity(cell, point, vec_dim); - } - } - } - - // -Div(eta B) * I contribution - for (int dim = 0; dim < num_grad_dim; ++dim) - { - _induction_flux[dim](cell, point, dim) -= div_eta_b; - } - - // every term has eta or grad(eta), so can scale once by mu_0 to - // recover \hat(eta) - for (int flux_dim = 0; flux_dim < num_grad_dim; ++flux_dim) - { - for (int vec_dim = 0; vec_dim < num_space_dim; ++vec_dim) - { - _induction_flux[vec_dim](cell, point, flux_dim) *= mu_0_inv; - } - } - - if (_solve_magn_corr) - { - for (int flux_dim = 0; flux_dim < num_grad_dim; ++flux_dim) - { - _magnetic_correction_potential_flux(cell, point, flux_dim) - = 0.0; - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INDUCTIONRESISTIVEFLUX_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.cpp deleted file mode 100644 index 6d7ad80..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MHDVortexProblemExact.hpp" -#include "VertexCFD_Closure_MHDVortexProblemExact_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::MHDVortexProblemExact) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp deleted file mode 100644 index 7b858a4..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_HPP -#define VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Exact solution for MHD vortex problem -//---------------------------------------------------------------------------// -template -class MHDVortexProblemExact : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - MHDVortexProblemExact(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& full_induction_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - int _ir_degree; - int _ir_index; - int _basis_index; - Kokkos::Array _vel_0; - Kokkos::Array _xy_0; - double _time; - PHX::MDField _ip_coords; - - public: - PHX::MDField _lagrange_pressure; - Kokkos::Array, num_space_dim> - _velocity; - Kokkos::Array, num_space_dim> - _induced_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact_impl.hpp deleted file mode 100644 index f022dfa..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact_impl.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_IMPL_HPP -#define VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MHDVortexProblemExact::MHDVortexProblemExact( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& full_induction_params) - : _ir_degree(ir.cubature_degree) - , _lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) -{ - const auto vel_0 - = full_induction_params.get>("velocity_0"); - const auto xy_0 - = full_induction_params.get>("center_0"); - for (int dim = 0; dim < 2; ++dim) - { - _vel_0[dim] = vel_0[dim]; - _xy_0[dim] = xy_0[dim]; - } - - // Add evaluated fields - this->addEvaluatedField(_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "Exact_velocity_"); - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _induced_magnetic_field, - "Exact_induced_magnetic_field_"); - - // Closure model name - this->setName("MHD Vortex Problem Exact Solution " - + std::to_string(num_space_dim) + "D."); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblemExact::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblemExact::evaluateFields( - typename Traits::EvalData workset) -{ - _time = workset.time; - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblemExact::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - using std::exp; - const double exp_one = exp(1.0); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Coordinnates - const double x = _ip_coords(cell, point, 0); - const double y = _ip_coords(cell, point, 1); - const double r2 = (x - _time * _vel_0[0] - _xy_0[0]) - * (x - _time * _vel_0[0] - _xy_0[0]) - + (y - _time * _vel_0[0] - _xy_0[1]) - * (y - _time * _vel_0[0] - _xy_0[1]); - - // Exact solutions - _lagrange_pressure(cell, point) - = 1.0 + 0.5 * exp_one * (1.0 - r2 * exp(-r2)); - - _induced_magnetic_field[0](cell, point) = exp(0.5 * (1.0 - r2)) - * (_xy_0[1] - y); - _induced_magnetic_field[1](cell, point) = exp(0.5 * (1.0 - r2)) - * (x - _xy_0[0]); - - _velocity[0](cell, point) = _induced_magnetic_field[0](cell, point) - + _vel_0[0]; - _velocity[1](cell, point) = _induced_magnetic_field[1](cell, point); - - if (num_space_dim == 3) - { - _induced_magnetic_field[2](cell, point) = 0.0; - _velocity[2](cell, point) = 0.0; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_MHDVORTEXPROBLEMEXACT_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.cpp deleted file mode 100644 index e6cdfc5..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MagneticCorrectionDampingSource.hpp" -#include "VertexCFD_Closure_MagneticCorrectionDampingSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::MagneticCorrectionDampingSource) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp deleted file mode 100644 index 1638c50..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_HPP -#define VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Damping term for the magnetic correction potential field -//---------------------------------------------------------------------------// -template -class MagneticCorrectionDampingSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - MagneticCorrectionDampingSource( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _damping_potential_source; - - private: - double _alpha; - - PHX::MDField - _scalar_magnetic_potential; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource_impl.hpp deleted file mode 100644 index f702662..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource_impl.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MagneticCorrectionDampingSource::MagneticCorrectionDampingSource( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _damping_potential_source("DAMPING_SOURCE_magnetic_correction_potential", - ir.dl_scalar) - , _alpha(mhd_props.magneticCorrectionDampingFactor()) - , _scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) -{ - // Evaluated fields - this->addEvaluatedField(_damping_potential_source); - - // Dependent fields - this->addDependentField(_scalar_magnetic_potential); - - this->setName("Magnetic Correction Damping Source"); -} - -//---------------------------------------------------------------------------// -template -void MagneticCorrectionDampingSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MagneticCorrectionDampingSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _scalar_magnetic_potential.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _damping_potential_source(cell, point) - = -_alpha * _scalar_magnetic_potential(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_MAGNETICCORRECTIONDAMPINGSOURCE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.cpp deleted file mode 100644 index c0ff1d5..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_MagneticPressure.hpp" -#include "VertexCFD_Closure_MagneticPressure_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::MagneticPressure) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp deleted file mode 100644 index e483376..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MAGNETICPRESSURE_HPP -#define VERTEXCFD_CLOSURE_MAGNETICPRESSURE_HPP - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Magnetic pressure for full induction MHD -//---------------------------------------------------------------------------// -template -class MagneticPressure : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - MagneticPressure(const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - double _magnetic_permeability; - Kokkos::Array, 3> - _total_magnetic_field; - - public: - PHX::MDField _magnetic_pressure; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_MAGNETICPRESSURE_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure_impl.hpp deleted file mode 100644 index 7f70b38..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure_impl.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_MAGNETICPRESSURE_IMPL_HPP -#define VERTEXCFD_CLOSURE_MAGNETICPRESSURE_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -MagneticPressure::MagneticPressure( - const panzer::IntegrationRule& ir, - const MHDProperties::FullInductionMHDProperties& mhd_props) - : _magnetic_permeability(mhd_props.vacuumMagneticPermeability()) - , _magnetic_pressure("magnetic_pressure", ir.dl_scalar) -{ - // Add dependent fields - Utils::addDependentVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - - // Add evaluated fields - this->addEvaluatedField(_magnetic_pressure); - - // Closure model name - this->setName("Magnetic Pressure"); -} - -//---------------------------------------------------------------------------// -template -void MagneticPressure::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MagneticPressure::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _magnetic_pressure.extent(1); - const int num_field_dim = _total_magnetic_field.size(); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _magnetic_pressure(cell, point) = 0.0; - for (int dim = 0; dim < num_field_dim; ++dim) - { - _magnetic_pressure(cell, point) - += _total_magnetic_field[dim](cell, point) - * _total_magnetic_field[dim](cell, point); - } - _magnetic_pressure(cell, point) /= 2.0 * _magnetic_permeability; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_MAGNETICPRESSURE_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.cpp deleted file mode 100644 index 4ec12fb..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_TotalMagneticField.hpp" -#include "VertexCFD_Closure_TotalMagneticField_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::TotalMagneticField) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp deleted file mode 100644 index 32ded79..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_HPP -#define VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute total magnetic field for full induction MHD -//---------------------------------------------------------------------------// -template -class TotalMagneticField : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - TotalMagneticField(const panzer::IntegrationRule& ir, - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - bool _uniform_external_field; - Kokkos::Array, - num_space_dim> - _induced_magnetic_field; - Kokkos::Array, 3> - _external_magnetic_field; - - public: - Kokkos::Array, 3> - _total_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.cpp deleted file mode 100644 index c7aa481..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_TotalMagneticFieldGradient.hpp" -#include "VertexCFD_Closure_TotalMagneticFieldGradient_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::TotalMagneticFieldGradient) diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp deleted file mode 100644 index fae751a..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_HPP -#define VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute total magnetic field gradient for full induction MHD -//---------------------------------------------------------------------------// -template -class TotalMagneticFieldGradient - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - TotalMagneticFieldGradient(const panzer::IntegrationRule& ir, - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - bool _uniform_external_field; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_induced_magnetic_field; - Kokkos::Array< - PHX::MDField, - 3> - _grad_external_magnetic_field; - - public: - Kokkos::Array< - PHX::MDField, - 3> - _grad_total_magnetic_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient_impl.hpp deleted file mode 100644 index 3fbdd90..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient_impl.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_IMPL_HPP -#define VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -TotalMagneticFieldGradient::TotalMagneticFieldGradient( - const panzer::IntegrationRule& ir, const std::string& gradient_prefix) - : _uniform_external_field(true) -{ - // Add dependent fields - Utils::addDependentVectorField( - *this, - ir.dl_vector, - _grad_induced_magnetic_field, - gradient_prefix + "GRAD_induced_magnetic_field_"); - if (!_uniform_external_field) - { - Utils::addDependentVectorField(*this, - ir.dl_vector, - _grad_external_magnetic_field, - "GRAD_external_magnetic_field_"); - } - - // Add evaluated fields - Utils::addEvaluatedVectorField( - *this, - ir.dl_vector, - _grad_total_magnetic_field, - gradient_prefix + "GRAD_total_magnetic_field_"); - - // Closure model name - this->setName("Total Magnetic Field Gradient" - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TotalMagneticFieldGradient::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TotalMagneticFieldGradient::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_total_magnetic_field[0].extent(1); - const int num_grad_dim = _grad_total_magnetic_field[0].extent(2); - const int num_field_dim = _grad_total_magnetic_field.size(); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - for (int grad_dim = 0; grad_dim < num_grad_dim; ++grad_dim) - { - _grad_total_magnetic_field[dim](cell, point, grad_dim) - = _grad_induced_magnetic_field[dim]( - cell, point, grad_dim); - } - } - - if (num_space_dim < num_field_dim) - { - for (int grad_dim = 0; grad_dim < num_grad_dim; ++grad_dim) - { - _grad_total_magnetic_field[2](cell, point, grad_dim) = 0.0; - } - } - - if (!_uniform_external_field) - { - for (int field_dim = 0; field_dim < num_field_dim; ++field_dim) - { - for (int grad_dim = 0; grad_dim < num_grad_dim; ++grad_dim) - { - _grad_total_magnetic_field[field_dim]( - cell, point, grad_dim) - += _grad_external_magnetic_field[field_dim]( - cell, point, grad_dim); - } - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_TOTALMAGNETICFIELDGRADIENT_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField_impl.hpp deleted file mode 100644 index 393fc41..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField_impl.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_IMPL_HPP -#define VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -TotalMagneticField::TotalMagneticField( - const panzer::IntegrationRule& ir, const std::string& field_prefix) -{ - // Add dependent fields - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _induced_magnetic_field, - field_prefix + "induced_magnetic_field_"); - Utils::addDependentVectorField(*this, - ir.dl_scalar, - _external_magnetic_field, - "external_magnetic_field_"); - // Add evaluated fields - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _total_magnetic_field, "total_magnetic_field_"); - - // Closure model name - this->setName("Total Magnetic Field " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void TotalMagneticField::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TotalMagneticField::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _total_magnetic_field[0].extent(1); - const int num_field_dim = _total_magnetic_field.size(); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - _total_magnetic_field[dim](cell, point) - = _induced_magnetic_field[dim](cell, point) - + _external_magnetic_field[dim](cell, point); - } - - if (num_space_dim < num_field_dim) - { - _total_magnetic_field[2](cell, point) - = _external_magnetic_field[2](cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_TOTALMAGNETICFIELD_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp deleted file mode 100644 index 3c3a4da..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_HPP -#define VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class FullInductionFactory -{ - public: - static constexpr int num_space_dim = NumSpaceDim; - - void buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& closure_params, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators); -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_HPP diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian2d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian2d.cpp deleted file mode 100644 index 2d345da..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian3d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian3d.cpp deleted file mode 100644 index 777f2ff..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Hessian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian2d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian2d.cpp deleted file mode 100644 index 36a3339..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian3d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian3d.cpp deleted file mode 100644 index f16a9a0..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Jacobian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual2d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual2d.cpp deleted file mode 100644 index c3245bb..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual3d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual3d.cpp deleted file mode 100644 index 75fce2c..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Residual3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent2d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent2d.cpp deleted file mode 100644 index 9a34c79..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent3d.cpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent3d.cpp deleted file mode 100644 index 05fc7fc..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_Tangent3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_FullInductionClosureModelFactory.hpp" -#include "VertexCFD_FullInductionClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - FullInductionFactory; diff --git a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_impl.hpp b/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_impl.hpp deleted file mode 100644 index 9e925e7..0000000 --- a/src/full_induction_mhd_solver/closure_models/VertexCFD_FullInductionClosureModelFactory_impl.hpp +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_IMPL_HPP -#define VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_IMPL_HPP - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include "closure_models/VertexCFD_Closure_ConstantScalarField.hpp" - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionModelErrorNorms.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp" -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -void FullInductionFactory::buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& closure_params, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators) -{ - // Fluid properties - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params - = FluidProperties::ConstantFluidProperties(fluid_prop_list); - - // Properties used by full induction MHD closure models - const auto full_induction_params - = user_params.sublist("Full Induction MHD Properties"); - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - const bool build_magn_corr = mhd_props.buildMagnCorr(); - - // Closure models - if (closure_type == "InductionConvectiveFlux") - { - auto eval = Teuchos::rcp( - new InductionConvectiveFlux( - *ir, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "InductionResistiveFlux") - { - auto eval = Teuchos::rcp( - new InductionResistiveFlux( - *ir, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "MagneticPressure") - { - auto eval = Teuchos::rcp( - new MagneticPressure(*ir, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "FullInductionTimeDerivative") - { - std::vector mhd_names_list_vct; - for (int dim = 0; dim < num_space_dim; ++dim) - { - Teuchos::ParameterList ind_names_list; - ind_names_list.set( - "Field Name", "induced_magnetic_field_" + std::to_string(dim)); - ind_names_list.set("Equation Name", - "induction_" + std::to_string(dim)); - mhd_names_list_vct.push_back(ind_names_list); - } - if (build_magn_corr) - { - Teuchos::ParameterList magn_corr_names_list; - magn_corr_names_list.set("Field Name", "scalar_magnetic_potential"); - magn_corr_names_list.set("Equation Name", - "magnetic_correction_potential"); - mhd_names_list_vct.push_back(magn_corr_names_list); - } - for (auto& mhd_names_list : mhd_names_list_vct) - { - auto eval_time = Teuchos::rcp( - new IncompressibleVariableTimeDerivative( - *ir, mhd_names_list)); - evaluators->push_back(eval_time); - } - found_model = true; - } - - if (closure_type == "TotalMagneticField") - { - auto eval = Teuchos::rcp( - new TotalMagneticField( - *ir)); - evaluators->push_back(eval); - auto eval_grad = Teuchos::rcp( - new TotalMagneticFieldGradient( - *ir)); - evaluators->push_back(eval_grad); - found_model = true; - } - - if (closure_type == "MHDVortexProblemExact") - { - auto eval = Teuchos::rcp( - new MHDVortexProblemExact( - *ir, full_induction_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "FullInductionModelErrorNorm") - { - auto eval = Teuchos::rcp( - new FullInductionModelErrorNorms( - *ir)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "DivergenceCleaningSource") - { - auto eval = Teuchos::rcp( - new DivergenceCleaningSource( - *ir)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "GodunovPowellSource") - { - auto eval = Teuchos::rcp( - new GodunovPowellSource( - *ir, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "MagneticCorrectionDampingSource") - { - auto eval = Teuchos::rcp( - new MagneticCorrectionDampingSource( - *ir, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "FullInductionLocalTimeStepSize") - { - auto eval = Teuchos::rcp( - new FullInductionLocalTimeStepSize( - *ir, incompressible_fluidprop_params, mhd_props)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "Resistivity") - { - if (mhd_props.variableResistivity()) - { - throw std::runtime_error( - "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."); - } - else - { - auto eval = Teuchos::rcp( - new ConstantScalarField( - *ir, "resistivity", mhd_props.resistivity())); - evaluators->push_back(eval); - found_model = true; - } - } - - if (closure_type == "InductionConstantSource") - { - auto eval = Teuchos::rcp( - new InductionConstantSource( - *ir, closure_params)); - evaluators->push_back(eval); - found_model = true; - } - - // Initialize 'error_msg' with list of closure models for induction MHD - // equations - error_msg = "DivergenceCleaningSource\n"; - error_msg += "FullInductionLocalTimeStepSize\n"; - error_msg += "FullInductionModelErrorNorm\n"; - error_msg += "FullInductionTimeDerivative\n"; - error_msg += "GodunovPowellSource\n"; - error_msg += "InductionConstantSource\n"; - error_msg += "InductionConvectiveFlux\n"; - error_msg += "InductionResistiveFlux\n"; - error_msg += "MagneticCorrectionDampingSource\n"; - error_msg += "MagneticPressure\n"; - error_msg += "MHDVortexProblemExact\n"; - error_msg += "Resistivity\n"; - error_msg += "TotalMagneticField\n"; -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_FULLINDUCTIONCLOSUREMODELFACTORY_IMPL_HPP diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/CMakeLists.txt b/src/full_induction_mhd_solver/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index b938849..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - MagneticPressure - FullInductionTimeDerivative - InductionConvectiveFlux - InductionResistiveFlux - TotalMagneticField - TotalMagneticFieldGradient - MHDVortexProblemExact - FullInductionErrorNorms - DivergenceCleaningSource - GodunovPowellSource - MagneticCorrectionDampingSource - FullInductionLocalTimeStepSize - ResistivityFactory - InductionConstantSource - ) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/divergenceCleaningSource_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/divergenceCleaningSource_reference.py deleted file mode 100644 index f150e0c..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/divergenceCleaningSource_reference.py +++ /dev/null @@ -1,36 +0,0 @@ -import numpy as np -import math - - -def get_exp_val_at_point(qp, num_grad_dim, num_space_dim): - # set the dependencies - max_dim = max(num_grad_dim, num_space_dim) - grad_psi = np.zeros(max_dim) - vel = np.zeros(max_dim) - - for d in range(num_grad_dim): - grad_psi[d] = pow(-0.9, d + 1) * (qp + d + 1) - for d in range(num_space_dim): - vel[d] = pow(-0.6, d) * (qp + d + 1) - - exp_pot_src_val = -np.dot(vel, grad_psi) - - return exp_pot_src_val - - -def get_exp_values(num_grad_dim, num_space_dim): - # get expected values at maximum of 8 quadrature points - max_num_qp = 8 - exp_pot_src_vals = np.zeros(max_num_qp) - for qp in range(max_num_qp): - exp_pot_src_vals[qp] = get_exp_val_at_point(qp, num_grad_dim, - num_space_dim) - - print("Divergence Cleaning Source (num_grad_dim = ", num_grad_dim, - ", num_space_dim = ", num_space_dim, ")") - print(" exp_src_magn_pot[", max_num_qp, "] = ", exp_pot_src_vals, "\n") - - -get_exp_values(2, 2) -get_exp_values(2, 3) -get_exp_values(3, 3) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/error_norms_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/error_norms_reference.py deleted file mode 100644 index ae2cb2f..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/error_norms_reference.py +++ /dev/null @@ -1,17 +0,0 @@ -import math - -# computational/exact solution -comp = [0.3, 0.4, 0.5] -exact = [0.7, 0.9, 1.1] - -var_id = range(len(comp)) - -# L1 error norm -print("L1 norm of vec{B}):") -for i in var_id: - print(abs(comp[i] - exact[i])) - -# L2 error norm -print("L2 norm vec{B}:") -for i in var_id: - print((comp[i] - exact[i])**2.0) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/fullInductionLocalTimeStepSize_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/fullInductionLocalTimeStepSize_reference.py deleted file mode 100644 index 9d8d3a0..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/fullInductionLocalTimeStepSize_reference.py +++ /dev/null @@ -1,39 +0,0 @@ -import math - -# Dependent values -u = -1.5 -v = 2.0 -w = -0.5 - -b_0 = 1.1 -b_1 = -1.2 -b_2 = 1.3 - -rho = 0.9 -mu_0 = 0.1 - -# Alfven speeds -c_a_0 = abs(b_0) / math.sqrt(mu_0 * rho) -c_a_1 = abs(b_1) / math.sqrt(mu_0 * rho) -c_a_2 = abs(b_2) / math.sqrt(mu_0 * rho) - -h0 = 0.25 -h1 = 0.5 -h2 = 0.75 - -# 2-D case, c_h = 0.1 -c_h = 0.1 -dt = 1.0 / ((abs(u) + max(c_a_0, c_h)) / h0 + (abs(v) + max(c_a_1, c_h)) / h1) -print("2-D dt, c_h = 0.1: ", dt) - -# 3-D case, large c_h -c_h = 5.0 -dt = 1.0 / ((abs(u) + max(c_a_0, c_h)) / h0 + (abs(v) + max(c_a_1, c_h)) / h1 + - (abs(w) + max(c_a_2, c_h)) / h2) -print("3-D dt, c_h = 5.0: ", dt) - -# 3-D case, no c_h contribution -c_h = 5.0 -dt = 1.0 / ((abs(u) + c_a_0) / h0 + (abs(v) + c_a_1) / h1 + - (abs(w) + c_a_2) / h2) -print("3-D dt, no c_h: ", dt) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/godunovPowellSource_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/godunovPowellSource_reference.py deleted file mode 100644 index eb94f9b..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/godunovPowellSource_reference.py +++ /dev/null @@ -1,42 +0,0 @@ -import numpy as np -import math - - -def get_exp_val_at_point(qp, num_space_dim): - # set the dependencies - mu_0 = 0.05 - div_mag = pow(-1.0, qp) * (qp + 1.1) - vel = np.zeros(3) - mag = np.zeros(3) - for d in range(num_space_dim): - vel[d] = pow(-0.6, d) * (qp + d + 1.2) - for d in range(3): - mag[d] = pow(-0.9, d + 1) * (qp + d + 1.3) - - exp_mtm_src_val = np.zeros(num_space_dim) - exp_ind_src_val = np.zeros(num_space_dim) - for d in range(num_space_dim): - exp_mtm_src_val[d] = -div_mag * mag[d] / mu_0 - exp_ind_src_val[d] = -div_mag * vel[d] - - return (exp_mtm_src_val, exp_ind_src_val) - - -def get_exp_values(num_space_dim): - # get expected values at maximum of 8 quadrature points - max_num_qp = 8 - exp_mtm_src_vals = np.zeros((max_num_qp, num_space_dim)) - exp_ind_src_vals = np.zeros((max_num_qp, num_space_dim)) - for qp in range(max_num_qp): - (exp_mtm_src_vals[qp], - exp_ind_src_vals[qp]) = get_exp_val_at_point(qp, num_space_dim) - - print("Divergence Cleaning Source (num_space_dim = ", num_space_dim, ")") - print(" exp_src_mtm[", max_num_qp, "][", num_space_dim, "] =\n", - exp_mtm_src_vals, "\n") - print(" exp_src_ind[", max_num_qp, "][", num_space_dim, "] =\n", - exp_ind_src_vals, "\n") - - -get_exp_values(2) -get_exp_values(3) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionConvectiveFlux_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionConvectiveFlux_reference.py deleted file mode 100644 index cba5287..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionConvectiveFlux_reference.py +++ /dev/null @@ -1,80 +0,0 @@ -import numpy as np - -# set the dependencies -v = np.array((1.25, 1.5, 1.125)) -b = np.array((1.1, 2.1, 3.1)) - -psi = 0.4 -pmag = 0.8 -mu_0 = .05 -c_h = 5.0 - -mtm_0_flux = np.array((0.125, -0.26, 0.377)) -mtm_1_flux = np.array((0.250, -0.52, 0.754)) -mtm_2_flux = np.array((0.375, -0.78, 1.131)) - -# compute the induction contribution to momentum flux -# rho_u fluxes in x, y, z directions -mtm_0_flux[0] += -b[0] * b[0] / mu_0 + pmag -mtm_0_flux[1] += -b[1] * b[0] / mu_0 -mtm_0_flux[2] += -b[2] * b[0] / mu_0 -# rho_v fluxes in x, y, z directions -mtm_1_flux[0] += -b[0] * b[1] / mu_0 -mtm_1_flux[1] += -b[1] * b[1] / mu_0 + pmag -mtm_1_flux[2] += -b[2] * b[1] / mu_0 -# r_ho_w fluxes in x, y, z directions -mtm_2_flux[0] += -b[0] * b[2] / mu_0 -mtm_2_flux[1] += -b[1] * b[2] / mu_0 -mtm_2_flux[2] += -b[2] * b[2] / mu_0 + pmag - -# compute induction equation flux (without divergence cleaning) -# x direction flux for the induction equations [F_x(B_0), F_x(B_1), F_x(B_2)] -ind_flux_x = [ - v[0] * b[0] - b[0] * v[0], v[0] * b[1] - b[0] * v[1], - v[0] * b[2] - b[0] * v[2] -] -# y direction flux for the induction equations [F_y(B_0), F_y(B_1), F_y(B_2)] -ind_flux_y = [ - v[1] * b[0] - b[1] * v[0], v[1] * b[1] - b[1] * v[1], - v[1] * b[2] - b[1] * v[2] -] -# z direction flux for the induction equations [F_z(B_0), F_z(B_1), F_z(B_2)] -ind_flux_z = [ - v[2] * b[0] - b[2] * v[0], v[2] * b[1] - b[2] * v[1], - v[2] * b[2] - b[2] * v[2] -] - -# print expected momentum fluxes -print("\nExpected fluxes (no divergence cleaning)\n") -print("\tmtm_0_flux = ", mtm_0_flux) -print("\tmtm_1_flux = ", mtm_1_flux) -print("\tmtm_2_flux = ", mtm_2_flux) - -# B_0 fluxes -print("\n\tind_0_flux = ", ind_flux_x[0], ind_flux_y[0], ind_flux_z[0]) -# B_1 fluxes -print("\tind_1_flux = ", ind_flux_x[1], ind_flux_y[1], ind_flux_z[1]) -# B_2 fluxes -print("\tind_2_flux = ", ind_flux_x[2], ind_flux_y[2], ind_flux_z[2]) - -# add divergence cleaning contributions -ind_flux_x[0] += c_h * psi -ind_flux_y[1] += c_h * psi -ind_flux_z[2] += c_h * psi -psi_flux = b * c_h - -# print expected momentum fluxes -print("\nExpected fluxes (with divergence cleaning)\n") -print("\tmtm_0_flux = ", mtm_0_flux) -print("\tmtm_1_flux = ", mtm_1_flux) -print("\tmtm_2_flux = ", mtm_2_flux) - -# B_0 fluxes -print("\n\tind_0_flux = ", ind_flux_x[0], ind_flux_y[0], ind_flux_z[0]) -# B_1 fluxes -print("\tind_1_flux = ", ind_flux_x[1], ind_flux_y[1], ind_flux_z[1]) -# B_2 fluxes -print("\tind_2_flux = ", ind_flux_x[2], ind_flux_y[2], ind_flux_z[2]) - -print("\n\tpsi_flux = ", psi_flux) -print() diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionResitiveFlux_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionResitiveFlux_reference.py deleted file mode 100644 index 3cd10f6..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/inductionResitiveFlux_reference.py +++ /dev/null @@ -1,71 +0,0 @@ -import numpy as np -import math - - -def vector_divergence(grad_vec, dim): - div = 0 - for d in range(dim): - div += grad_vec[d][d] - return div - - -def resistive_flux(eta, b, num_grad_dim, const_eta): - # set up gradient test values - grad_eta = np.empty(num_grad_dim) - grad_b = np.empty((3, num_grad_dim)) - for dim in range(num_grad_dim): - if (const_eta): - grad_eta[dim] = 0.0 - else: - grad_eta[dim] = eta * math.pow(-1, dim + 1) * (dim + 2) - for i in range(3): - grad_b[i][dim] = b[i] * math.pow(-1, dim) * (dim + 1) - - # first term: eta * grad_b - eta_times_grad_b = eta * grad_b - - # second_term - # div(eta B) = eta div(B) + grad(eta).B - div_b = 0.0 - grad_eta_dot_b = 0.0 - for dim in range(num_grad_dim): - div_b += grad_b[dim][dim] - grad_eta_dot_b += grad_eta[dim] * b[dim] - div_eta_b = np.zeros((3, num_grad_dim)) - for dim in range(num_grad_dim): - div_eta_b[dim][dim] = eta * div_b + grad_eta_dot_b - - ind_flux = eta_times_grad_b - div_eta_b - - # third term B otimes grad(eta) - # G_x = B_x d_eta/dx G_y = B_y d_eta/dx G_z = B_z d_eta/dx - # B_x d_eta/dy B_y d_eta/dy B_z d_eta/dy - # B_x d_eta/dz B_y d_eta/dz B_z d_eta/dz - for i in range(num_grad_dim): - for j in range(num_grad_dim): - ind_flux[i][j] += b[j] * grad_eta[i] - - print("\ninduction eqn resistive flux ", num_grad_dim, "D:\n") - print("\tind_0_flux = ", ind_flux[0]) - print("\tind_1_flux = ", ind_flux[1]) - print("\tind_2_flux = ", ind_flux[2]) - - -# set the dependencies -eta = 0.16 -b = np.array((1.1, 2.1, 3.1)) -mu_0 = 0.05 - -hat_eta = eta / mu_0 - -# 2D test, variable resistivity -resistive_flux(hat_eta, b, 2, False) - -# 3D test, variable resistivity -resistive_flux(hat_eta, b, 3, False) - -# 2D test, constant resistivity -resistive_flux(hat_eta, b, 2, True) - -# 3D test, constant resistivity -resistive_flux(hat_eta, b, 3, True) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/doc/mhd_vortex_problem_exact_reference.py b/src/full_induction_mhd_solver/closure_models/unit_test/doc/mhd_vortex_problem_exact_reference.py deleted file mode 100644 index e0e3924..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/doc/mhd_vortex_problem_exact_reference.py +++ /dev/null @@ -1,36 +0,0 @@ -import math - -# -x = 0.2 -y = 0.4 -x0 = 0.1 -y0 = -0.2 -dx = x - x0 -dy = y - y0 - -# -u0 = 2.0 -v0 = -0.5 - -# -time = 0.6 - -# -r = (dx - time * u0) * (dx - time * u0) -r += (dy - time * u0) * (dy - time * u0) -r = math.sqrt(r) -r2 = r * r - -# -lp = 1.0 + 0.5 * math.exp(1.0) * (1.0 - r2 * math.exp(-r2)) -print("Exact lagrange pressure:", lp) - -mg0 = -math.exp(0.5 * (1.0 - r2)) * dy -mg1 = math.exp(0.5 * (1.0 - r2)) * dx -print("Exact x-magnetic field:", mg0) -print("Exact y-magnetic field:", mg1) - -v0 = mg0 + u0 -v1 = mg1 -print("Exact x-velocity:", v0) -print("Exact y-velocity:", v1) diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstDivergenceCleaningSource.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstDivergenceCleaningSource.cpp deleted file mode 100644 index 84ce66f..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstDivergenceCleaningSource.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_DivergenceCleaningSource.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - static constexpr int num_space_dim = NumSpaceDim; - using scalar_type = typename EvalType::ScalarT; - - Kokkos::Array, - num_space_dim> - velocity; - PHX::MDField - grad_scalar_magnetic_potential; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_scalar_magnetic_potential("GRAD_scalar_magnetic_potential", - ir.dl_vector) - { - this->addEvaluatedField(grad_scalar_magnetic_potential); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, velocity, "velocity_"); - - this->setName("Divergence Cleaning Source Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "divergence cleaning source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_scalar_magnetic_potential.extent(1); - const int num_grad_dim = grad_scalar_magnetic_potential.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_grad_dim; ++dim) - { - grad_scalar_magnetic_potential(c, qp, dim) = pow(-0.9, dim + 1) - * (qp + dim + 1); - } - for (int dim = 0; dim < num_space_dim; ++dim) - { - velocity[dim](c, qp) = pow(-0.6, dim) * (qp + dim + 1); - } - } - } -}; - -template -void testEval(const int num_grad_dim) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize class object to test - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - auto eval = Teuchos::rcp( - new ClosureModel::DivergenceCleaningSource(ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField( - eval->_div_cleaning_potential_source); - - test_fixture.evaluate(); - - const auto src_magn_pot = test_fixture.getTestFieldData( - eval->_div_cleaning_potential_source); - - const int num_point = ir.num_points; - - const double exp_src_magn_pot_2d[8] - = {2.844, 7.974, 15.876, 26.55, 39.996, 56.214, 75.204, 96.966}; - const double exp_src_magn_pot_3d[8] = { - 5.20596, 12.17304, 22.437, 35.99784, 52.85556, 73.01016, 96.46164, 123.21}; - const auto exp_src_magn_pot = num_grad_dim == 2 ? exp_src_magn_pot_2d - : exp_src_magn_pot_3d; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_src_magn_pot[qp], fieldValue(src_magn_pot, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource2Vel2D, residual_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource2Vel2D, jacobian_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource3Vel2D, residual_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource3Vel2D, jacobian_test) -{ - testEval(2); -} -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource3D, residual_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -TEST(DivergenceCleaningSource3D, jacobian_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.type_name = "DivergenceCleaningSource"; - test_fixture.eval_name = "Divergence Cleaning Source " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::DivergenceCleaningSource, - num_space_dim>(); -} - -TEST(DivergenceCleaningSource_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(DivergenceCleaningSource_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(DivergenceCleaningSource_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(DivergenceCleaningSource_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionErrorNorms.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionErrorNorms.cpp deleted file mode 100644 index e1c3e64..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionErrorNorms.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - // exact solution - Kokkos::Array, num_space_dim> - exact_induced_magn_field; - - // numerical solution - Kokkos::Array, - num_space_dim> - induced_magn_field; - - Dependencies(const panzer::IntegrationRule& ir) - { - // exact solution - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - exact_induced_magn_field, - "Exact_induced_magnetic_field_"); - - // numerical solution - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, induced_magn_field, "induced_magnetic_field_"); - - this->setName("FullInduction Error Norms Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - // assign prescribed values to exact and numerical solution - for (int i = 0; i < num_space_dim; ++i) - { - exact_induced_magn_field[i].deep_copy(0.3 + 0.1 * i); - induced_magn_field[i].deep_copy(0.7 + 0.2 * i); - } - } -}; - -template -void testEval() -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - const auto deps - = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Create evaluator. - const auto eval = Teuchos::rcp( - new ClosureModel::FullInductionModelErrorNorms(ir)); - test_fixture.registerEvaluator(eval); - - // Add required test fields. - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(eval->_L1_error_induced[dim]); - test_fixture.registerTestField(eval->_L2_error_induced[dim]); - } - - // Evaluate - test_fixture.evaluate(); - - // Check the values - const auto L1_error_induced_0 - = test_fixture.getTestFieldData(eval->_L1_error_induced[0]); - const auto L1_error_induced_1 - = test_fixture.getTestFieldData(eval->_L1_error_induced[1]); - const auto L2_error_induced_0 - = test_fixture.getTestFieldData(eval->_L2_error_induced[0]); - const auto L2_error_induced_1 - = test_fixture.getTestFieldData(eval->_L2_error_induced[1]); - - // Reference values - const double L1_ref[3] = {0.39999999999999997, 0.5, 0.6000000000000001}; - const double L2_ref[3] = {0.15999999999999998, 0.25, 0.3600000000000001}; - - // Check the L1/L2 error solutions - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(L1_ref[0], fieldValue(L1_error_induced_0, 0, qp)); - EXPECT_DOUBLE_EQ(L1_ref[1], fieldValue(L1_error_induced_1, 0, qp)); - - EXPECT_DOUBLE_EQ(L2_ref[0], fieldValue(L2_error_induced_0, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[1], fieldValue(L2_error_induced_1, 0, qp)); - - if (num_space_dim == 3) - { - const auto L1_error_induced_2 - = test_fixture.getTestFieldData( - eval->_L1_error_induced[2]); - const auto L2_error_induced_2 - = test_fixture.getTestFieldData( - eval->_L2_error_induced[2]); - - EXPECT_DOUBLE_EQ(L1_ref[2], fieldValue(L1_error_induced_2, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[2], fieldValue(L2_error_induced_2, 0, qp)); - } - } -} - -//---------------------------------------------------------------------------// -TEST(FullInductionL1L2Isothermal_Error2D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionL1L2Isothermal_Error2D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionL1L2Isothermal_Error3D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionL1L2Isothermal_Error3D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "FullInductionModelErrorNorm"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.user_params.sublist("Full Induction MHD Properties"); - if (num_space_dim == 2) - test_fixture.eval_name = "Full Induction Model Error Norms 2D"; - else if (num_space_dim == 3) - test_fixture.eval_name = "Full Induction Model Error Norms 3D"; - test_fixture.template buildAndTest< - ClosureModel::FullInductionModelErrorNorms, - num_space_dim>(); -} - -TEST(FullInductionErrorNorms_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(FullInductionErrorNorms_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(FullInductionErrorNorms_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(FullInductionErrorNorms_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionLocalTimeStepSize.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionLocalTimeStepSize.cpp deleted file mode 100644 index 5ac38b5..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionLocalTimeStepSize.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_FullInductionLocalTimeStepSize.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u; - double _v; - double _w; - Kokkos::Array _h; - - PHX::MDField _element_length; - Kokkos::Array, 3> - _velocity; - Kokkos::Array, 3> - _tot_magn_field; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w, - const Kokkos::Array h) - : _u(u) - , _v(v) - , _w(w) - , _h(h) - , _element_length("element_length", ir.dl_vector) - { - this->addEvaluatedField(_element_length); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "velocity_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _tot_magn_field, "total_magnetic_field_"); - this->setName( - "Full Induction Local Time Step Size Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - _velocity[0].deep_copy(_u); - _velocity[1].deep_copy(_v); - _velocity[2].deep_copy(_w); - _tot_magn_field[0].deep_copy(1.1); - _tot_magn_field[1].deep_copy(-1.2); - _tot_magn_field[2].deep_copy(1.3); - Kokkos::parallel_for( - "test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - int num_point = _element_length.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - _element_length(c, qp, 0) = _h[0]; - _element_length(c, qp, 1) = _h[1]; - _element_length(c, qp, 2) = _h[2]; - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_magn_corr) -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Eval dependencies. - const double nan_val = std::numeric_limits::quiet_NaN(); - const double u = -1.5; - const double v = 2.0; - const double w = num_space_dim == 3 ? -0.5 : nan_val; - const Kokkos::Array h - = {0.25, 0.5, num_space_dim == 3 ? 0.75 : nan_val}; - - auto dep_eval = Teuchos::rcp( - new Dependencies(*test_fixture.ir, u, v, w, h)); - test_fixture.registerEvaluator(dep_eval); - - Teuchos::ParameterList fluid_params; - fluid_params.set("Density", 0.9); - fluid_params.set("Kinematic viscosity", nan_val); - fluid_params.set("Artificial compressibility", nan_val); - fluid_params.set("Build Temperature Equation", false); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params - = FluidProperties::ConstantFluidProperties(fluid_params); - - const double c_h = num_space_dim == 2 ? 0.1 : 5.0; - Teuchos::ParameterList full_induction_params; - full_induction_params.set("Vacuum Magnetic Permeability", 0.1); - full_induction_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - full_induction_params.set("Hyperbolic Divergence Cleaning Speed", c_h); - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - - // Create test evaluator. - auto dt_eval = Teuchos::rcp( - new ClosureModel::FullInductionLocalTimeStepSize( - *test_fixture.ir, incompressible_fluidprop_params, mhd_props)); - test_fixture.registerEvaluator(dt_eval); - - // Add required test fields. - test_fixture.registerTestField(dt_eval->_local_dt); - - // Evaluate test fields. - test_fixture.evaluate(); - - // Check the test fields. - auto local_dt_result - = test_fixture.getTestFieldData(dt_eval->_local_dt); - - const int num_qp = num_space_dim == 2 ? 4 : 8; - const double result = num_space_dim == 2 ? 0.030612244897959186 - : build_magn_corr ? 0.02112676056338028 - : 0.02556818181818182; - for (int qp = 0; qp < num_qp; ++qp) - EXPECT_DOUBLE_EQ(result, fieldValue(local_dt_result, 0, qp)); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSize2D, residual_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSize2D, jacobian_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSize3D, residual_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSize3D, jacobian_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSizeNoDivCleaning3D, residual_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(FullInductionLocalTimeStepSizeNoDivCleaning3D, jacobian_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "FullInductionLocalTimeStepSize"; - test_fixture.eval_name = "Full Induction Local Time Step Size"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.template buildAndTest< - ClosureModel::FullInductionLocalTimeStepSize, - num_space_dim>(); -} - -TEST(FullInductionLocalTimeStepSize_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(FullInductionLocalTimeStepSize_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionTimeDerivative.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionTimeDerivative.cpp deleted file mode 100644 index d2a06ec..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstFullInductionTimeDerivative.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -//-----------------------------------------------------------------// -template -void testFactory(const bool build_magn_corr) -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "FullInductionTimeDerivative"; - std::vector eval_names; - for (int dim = 0; dim < num_space_dim; ++dim) - { - eval_names.push_back("induction_" + std::to_string(dim) - + " Incompressible Time Derivative 2D"); - } - if (build_magn_corr) - { - eval_names.push_back( - "magnetic_correction_potential Incompressible Time Derivative 2D"); - } - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Build Magnetic Correction Potential Equation", build_magn_corr) - .set("Hyperbolic Divergence Cleaning Speed", 1.1); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - - test_fixture.num_evaluators = eval_names.size(); - for (int ind = 0; ind < test_fixture.num_evaluators; ++ind) - { - test_fixture.eval_index = ind; - test_fixture.eval_name = eval_names[ind]; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleVariableTimeDerivative, - num_space_dim>(); - } -} - -TEST(FullInductionTimeDerivative_Factory2D, residual_test) -{ - testFactory(false); -} - -TEST(FullInductionTimeDerivative_Factory2D, jacobian_test) -{ - testFactory(false); -} - -TEST(FullInductionTimeDerivative_Factory3D, residual_test) -{ - testFactory(false); -} - -TEST(FullInductionTimeDerivative_Factory3D, jacobian_test) -{ - testFactory(false); -} - -TEST(FullInductionDivCleaningTimeDerivative_Factory2D, residual_test) -{ - testFactory(true); -} - -TEST(FullInductionDivCleaningTimeDerivative_Factory2D, jacobian_test) -{ - testFactory(true); -} - -TEST(FullInductionDivCleaningTimeDerivative_Factory3D, residual_test) -{ - testFactory(true); -} - -TEST(FullInductionDivCleaningTimeDerivative_Factory3D, jacobian_test) -{ - testFactory(true); -} -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstGodunovPowellSource.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstGodunovPowellSource.cpp deleted file mode 100644 index 1402add..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstGodunovPowellSource.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_GodunovPowellSource.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - static constexpr int num_space_dim = NumSpaceDim; - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField div_magn_field; - - Kokkos::Array, - num_space_dim> - velocity; - Kokkos::Array, 3> - tot_magn_field; - - Dependencies(const panzer::IntegrationRule& ir) - : div_magn_field("divergence_total_magnetic_field", ir.dl_scalar) - { - this->addEvaluatedField(div_magn_field); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, velocity, "velocity_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, tot_magn_field, "total_magnetic_field_"); - - this->setName("Godunov-Powell Source Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "godunov-powell source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = div_magn_field.extent(1); - const int num_field_dim = tot_magn_field.size(); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - div_magn_field(c, qp) = pow(-1.0, qp) * (qp + 1.1); - for (int dim = 0; dim < num_space_dim; ++dim) - { - velocity[dim](c, qp) = pow(-0.6, dim) * (qp + dim + 1.2); - } - for (int dim = 0; dim < num_field_dim; ++dim) - { - tot_magn_field[dim](c, qp) = pow(-0.9, dim + 1) - * (qp + dim + 1.3); - } - } - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Initialize class object to test - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - Teuchos::ParameterList full_induction_params; - full_induction_params.set("Vacuum Magnetic Permeability", 0.05); - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - - auto eval = Teuchos::rcp( - new ClosureModel::GodunovPowellSource(ir, mhd_props)); - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - eval->_godunov_powell_momentum_source[dim]); - test_fixture.registerTestField( - eval->_godunov_powell_induction_source[dim]); - } - - test_fixture.evaluate(); - - const auto src_mom_0 = test_fixture.getTestFieldData( - eval->_godunov_powell_momentum_source[0]); - const auto src_mom_1 = test_fixture.getTestFieldData( - eval->_godunov_powell_momentum_source[1]); - - const auto src_ind_0 = test_fixture.getTestFieldData( - eval->_godunov_powell_induction_source[0]); - const auto src_ind_1 = test_fixture.getTestFieldData( - eval->_godunov_powell_induction_source[1]); - - const int num_point = ir.num_points; - - const double exp_src_mom[8][3] = {{25.74, -40.986, 52.9254}, - {-86.94, 112.266, -131.6574}, - {184.14, -215.946, 239.5494}, - {-317.34, 352.026, -376.6014}, - {486.54, -520.506, 542.8134}, - {-691.74, 721.386, -738.1854}, - {932.94, -954.666, 962.7174}, - {-1210.14, 1220.346, -1216.4094}}; - - const double exp_src_ind[8][3] = {{-1.32, 1.452, -1.2672}, - {4.62, -4.032, 3.1752}, - {-9.92, 7.812, -5.8032}, - {17.22, -12.792, 9.1512}, - {-26.52, 18.972, -13.2192}, - {37.82, -26.352, 18.0072}, - {-51.12, 34.932, -23.5152}, - {66.42, -44.712, 29.7432}}; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_src_mom[qp][0], fieldValue(src_mom_0, 0, qp)); - EXPECT_DOUBLE_EQ(exp_src_mom[qp][1], fieldValue(src_mom_1, 0, qp)); - EXPECT_DOUBLE_EQ(exp_src_ind[qp][0], fieldValue(src_ind_0, 0, qp)); - EXPECT_DOUBLE_EQ(exp_src_ind[qp][1], fieldValue(src_ind_1, 0, qp)); - if (num_space_dim > 2) - { - const auto src_mom_2 = test_fixture.getTestFieldData( - eval->_godunov_powell_momentum_source[2]); - EXPECT_DOUBLE_EQ(exp_src_mom[qp][2], fieldValue(src_mom_2, 0, qp)); - const auto src_ind_2 = test_fixture.getTestFieldData( - eval->_godunov_powell_induction_source[2]); - EXPECT_DOUBLE_EQ(exp_src_ind[qp][2], fieldValue(src_ind_2, 0, qp)); - } - } -}; - -//-----------------------------------------------------------------// -TEST(GodunovPowellSource2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(GodunovPowellSource2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(GodunovPowellSource3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(GodunovPowellSource3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.type_name = "GodunovPowellSource"; - test_fixture.eval_name = "Godunov-Powell Source " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::GodunovPowellSource, - num_space_dim>(); -} - -TEST(GodunovPowellSource_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(GodunovPowellSource_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(GodunovPowellSource_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(GodunovPowellSource_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConstantSource.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConstantSource.cpp deleted file mode 100644 index d046af9..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConstantSource.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConstantSource.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize class object to test - Teuchos::Array ind_input_source(num_space_dim); - ind_input_source[0] = 0.1; - ind_input_source[1] = 0.2; - if (num_space_dim == 3) - ind_input_source[2] = 0.3; - Teuchos::ParameterList closure_params; - closure_params.set("Induction Source", ind_input_source); - auto eval = Teuchos::rcp( - new ClosureModel:: - InductionConstantSource( - ir, closure_params)); - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_induction_source[dim]); - - test_fixture.evaluate(); - - const auto fc_ind_0 - = test_fixture.getTestFieldData(eval->_induction_source[0]); - const auto fc_ind_1 - = test_fixture.getTestFieldData(eval->_induction_source[1]); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(ind_input_source[0], fieldValue(fc_ind_0, 0, qp)); - EXPECT_EQ(ind_input_source[1], fieldValue(fc_ind_1, 0, qp)); - if (num_space_dim > 2) // 3D mesh - { - const auto fc_ind_2 = test_fixture.getTestFieldData( - eval->_induction_source[2]); - EXPECT_EQ(ind_input_source[2], fieldValue(fc_ind_2, 0, qp)); - } - } -} - -//-----------------------------------------------------------------// -TEST(InductionConstantSource2D, Residual) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(InductionConstantSource2D, Jacobian) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(InductionConstantSource3D, Residual) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(InductionConstantSource3D, Jacobian) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - const Teuchos::Array ind_input_source(num_space_dim); - test_fixture.model_params.set("Induction Source", ind_input_source); - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.type_name = "InductionConstantSource"; - test_fixture.eval_name = "Induction Constant Source " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::InductionConstantSource, - num_space_dim>(); -} - -TEST(InductionConstantSourceFactory2D, Residual) -{ - testFactory(); -} - -TEST(InductionConstantSourceFactory2D, Jacobian) -{ - testFactory(); -} - -TEST(InductionConstantSourceFactory3D, Residual) -{ - testFactory(); -} - -TEST(InductionConstantSourceFactory3D, Jacobian) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConvectiveFlux.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConvectiveFlux.cpp deleted file mode 100644 index bd085f5..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionConvectiveFlux.cpp +++ /dev/null @@ -1,313 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionConvectiveFlux.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - const Kokkos::Array _f_rhov; - const Kokkos::Array _v; - const Kokkos::Array _b; - const double _psi; - const double _p_mag; - const bool _build_magn_corr; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - mtm_flux; - Kokkos::Array, - num_space_dim> - vel; - Kokkos::Array, 3> mag; - PHX::MDField magn_pot; - PHX::MDField magn_pres; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array& f_rhov, - const Kokkos::Array& v, - const Kokkos::Array& b, - const double psi, - const double p_mag, - const bool build_magn_corr, - const std::string& flux_pre, - const std::string& field_pre) - : _f_rhov(f_rhov) - , _v(v) - , _b(b) - , _psi(psi) - , _p_mag(p_mag) - , _build_magn_corr(build_magn_corr) - , magn_pot(field_pre + "scalar_magnetic_potential", ir.dl_scalar) - , magn_pres("magnetic_pressure", ir.dl_scalar) - { - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - mtm_flux, - flux_pre + "CONVECTIVE_FLUX_momentum_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, vel, field_pre + "velocity_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, mag, "total_magnetic_field_"); - this->addEvaluatedField(magn_pres); - if (_build_magn_corr) - { - this->addEvaluatedField(magn_pot); - } - - this->setName("Induction Convective Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "induction convective flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = mag[0].extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - for (int field_dim = 0; field_dim < 3; ++field_dim) - { - mag[field_dim](c, qp) = _b[field_dim]; - } - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - vel[vel_dim](c, qp) = _v[vel_dim]; - for (int flux_dim = 0; flux_dim < num_space_dim; ++flux_dim) - { - mtm_flux[vel_dim](c, qp, flux_dim) = _f_rhov[flux_dim] - * (vel_dim + 1); - } - } - magn_pres(c, qp) = _p_mag; - if (_build_magn_corr) - magn_pot(c, qp) = _psi; - } - } -}; - -template -void testEval(const bool build_magn_corr, - const std::string& flux_pre, - const std::string& field_pre) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize velocity components and dependents - const Kokkos::Array f_rhov = {0.125, -0.26, 0.377}; - const Kokkos::Array v = {1.25, 1.5, 1.125}; - const Kokkos::Array b = {1.1, 2.1, 3.1}; - const double psi = 0.4; - const double p_mag = 0.8; - - auto deps = Teuchos::rcp(new Dependencies( - ir, f_rhov, v, b, psi, p_mag, build_magn_corr, flux_pre, field_pre)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList full_induction_params; - full_induction_params.set("Vacuum Magnetic Permeability", 0.05); - full_induction_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - if (build_magn_corr) - { - full_induction_params.set("Hyperbolic Divergence Cleaning Speed", 5.0); - } - - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - - auto eval = Teuchos::rcp( - new ClosureModel:: - InductionConvectiveFlux( - ir, mhd_props, flux_pre, field_pre)); - - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(eval->_momentum_flux[dim]); - test_fixture.registerTestField(eval->_induction_flux[dim]); - } - if (build_magn_corr) - { - test_fixture.registerTestField( - eval->_magnetic_correction_potential_flux); - } - - test_fixture.evaluate(); - - // Expected values - const double exp_mom_0_flux[3] = {-23.275, -46.46, -67.823}; - const double exp_mom_1_flux[3] = {-45.95, -87.92, -129.446}; - const double exp_mom_2_flux[3] = {-67.825, -130.98, -190.269}; - - const double psi_cont = build_magn_corr ? 2. : 0.; - - const double exp_ind_0_flux[3] = {psi_cont, -0.9749999999999999, -2.6375}; - const double exp_ind_1_flux[3] = {0.9749999999999999, psi_cont, -2.2875}; - const double exp_ind_2_flux[3] = {2.6375, 2.2875, psi_cont}; - - const double exp_psi_flux[3] = {5.5, 10.5, 15.5}; - - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_momentum_flux[0]); - const auto fc_mom_1 - = test_fixture.getTestFieldData(eval->_momentum_flux[1]); - - const auto fc_ind_0 - = test_fixture.getTestFieldData(eval->_induction_flux[0]); - const auto fc_ind_1 - = test_fixture.getTestFieldData(eval->_induction_flux[1]); - - const int num_point = ir.num_points; - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_DOUBLE_EQ(exp_mom_0_flux[dim], - fieldValue(fc_mom_0, 0, qp, dim)); - EXPECT_DOUBLE_EQ(exp_mom_1_flux[dim], - fieldValue(fc_mom_1, 0, qp, dim)); - EXPECT_DOUBLE_EQ(exp_ind_0_flux[dim], - fieldValue(fc_ind_0, 0, qp, dim)); - EXPECT_DOUBLE_EQ(exp_ind_1_flux[dim], - fieldValue(fc_ind_1, 0, qp, dim)); - if (num_space_dim > 2) - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_momentum_flux[2]); - EXPECT_DOUBLE_EQ(exp_mom_2_flux[dim], - fieldValue(fc_mom_2, 0, qp, dim)); - const auto fc_ind_2 = test_fixture.getTestFieldData( - eval->_induction_flux[2]); - EXPECT_DOUBLE_EQ(exp_ind_2_flux[dim], - fieldValue(fc_ind_2, 0, qp, dim)); - } - - if (build_magn_corr) - { - const auto fc_psi = test_fixture.getTestFieldData( - eval->_magnetic_correction_potential_flux); - EXPECT_DOUBLE_EQ(exp_psi_flux[dim], - fieldValue(fc_psi, 0, qp, dim)); - } - } - } -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxNoCleaning2D, residual_test) -{ - testEval(false, "", ""); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxNoCleaning2D, jacobian_test) -{ - testEval(false, "", ""); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxNoCleaning3D, residual_test) -{ - testEval(false, "Foo_", "Bar_"); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxNoCleaning3D, jacobian_test) -{ - testEval(false, "Foo_", "Bar_"); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxDivCleaning2D, residual_test) -{ - testEval(true, "Foo_", "Bar_"); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxNoDivCleaning2D, jacobian_test) -{ - testEval(true, "Foo_", "Bar_"); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxDivCleaning3D, residual_test) -{ - testEval(true, "", ""); -} - -//-----------------------------------------------------------------// -TEST(InductionConvectiveFluxDivCleaning3D, jacobian_test) -{ - testEval(true, "", ""); -} -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.type_name = "InductionConvectiveFlux"; - test_fixture.eval_name = "Induction Convective Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::InductionConvectiveFlux, - num_space_dim>(); -} - -TEST(InductionConvectiveFlux_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(InductionConvectiveFlux_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(InductionConvectiveFlux_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(InductionConvectiveFlux_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionResistiveFlux.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionResistiveFlux.cpp deleted file mode 100644 index 89ffa7e..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstInductionResistiveFlux.cpp +++ /dev/null @@ -1,488 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_InductionResistiveFlux.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - const Kokkos::Array _b; - const double _res; - - Kokkos::Array, 3> mag; - Kokkos::Array< - PHX::MDField, - 3> - grad_mag; - PHX::MDField eta; - PHX::MDField grad_eta; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array& b, - const double res, - const std::string& grad_pre) - : _b(b) - , _res(res) - , eta("resistivity", ir.dl_scalar) - , grad_eta("GRAD_resistivity", ir.dl_vector) - { - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, mag, "total_magnetic_field_"); - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - grad_mag, - grad_pre + "GRAD_total_magnetic_field_"); - this->addEvaluatedField(eta); - this->addEvaluatedField(grad_eta); - - this->setName("Induction Resistive Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "induction resistive flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_eta.extent(1); - const int num_space_dim = grad_eta.extent(2); - - for (int qp = 0; qp < num_point; ++qp) - { - for (int field_dim = 0; field_dim < 3; ++field_dim) - { - mag[field_dim](c, qp) = _b[field_dim]; - } - eta(c, qp) = _res; - for (int grad_dim = 0; grad_dim < num_space_dim; ++grad_dim) - { - const int b_mult = pow(-1, grad_dim) * (grad_dim + 1); - for (int field_dim = 0; field_dim < 3; ++field_dim) - { - grad_mag[field_dim](c, qp, grad_dim) = _b[field_dim] - * b_mult; - } - const int eta_mult = pow(-1, grad_dim + 1) * (grad_dim + 2); - grad_eta(c, qp, grad_dim) = _res * eta_mult; - } - } - } -}; - -//-----------------------------------------------------------------// -template -void testEval(const Teuchos::ParameterList& test_params, - const std::string& flux_pre, - const std::string& grad_pre) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize velocity components and dependents - const Kokkos::Array b = {1.1, 2.1, 3.1}; - const double eta = 0.16; - - auto deps = Teuchos::rcp(new Dependencies(ir, b, eta, grad_pre)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList full_induction_params - = test_params.sublist("Full Induction MHD Properties"); - full_induction_params.set("Vacuum Magnetic Permeability", 0.05); - full_induction_params.set("Build Resistive Flux", true); - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - const auto build_magn_corr = mhd_props.buildMagnCorr(); - - auto eval = Teuchos::rcp( - new ClosureModel:: - InductionResistiveFlux( - ir, mhd_props, flux_pre, grad_pre)); - - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(eval->_induction_flux[dim]); - } - if (build_magn_corr) - { - test_fixture.registerTestField( - eval->_magnetic_correction_potential_flux); - } - - test_fixture.evaluate(); - - const auto exp_ind_0_flux - = test_params.get>("Exp Ind 0 Flux"); - const auto exp_ind_1_flux - = test_params.get>("Exp Ind 1 Flux"); - const auto exp_ind_2_flux - = test_params.get>("Exp Ind 2 Flux"); - - const auto fc_ind_0 - = test_fixture.getTestFieldData(eval->_induction_flux[0]); - const auto fc_ind_1 - = test_fixture.getTestFieldData(eval->_induction_flux[1]); - - const int num_point = ir.num_points; - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_DOUBLE_EQ(exp_ind_0_flux[dim], - fieldValue(fc_ind_0, 0, qp, dim)); - EXPECT_DOUBLE_EQ(exp_ind_1_flux[dim], - fieldValue(fc_ind_1, 0, qp, dim)); - if (num_space_dim > 2) - { - const auto fc_ind_2 = test_fixture.getTestFieldData( - eval->_induction_flux[2]); - EXPECT_DOUBLE_EQ(exp_ind_2_flux[dim], - fieldValue(fc_ind_2, 0, qp, dim)); - } - - if (build_magn_corr) - { - const auto fc_psi = test_fixture.getTestFieldData( - eval->_magnetic_correction_potential_flux); - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_psi, 0, qp, dim)); - } - } - } -} - -//-----------------------------------------------------------------// -template -void testEval2D(const bool build_magn_corr, const bool var_resistivity) -{ - // Expected values 2D - // Variable resistivity - const double nanval = std::numeric_limits::signaling_NaN(); - const Teuchos::Array exp_ind_0_flux_var_eta( - {-6.72, -20.48, nanval}); - const Teuchos::Array exp_ind_1_flux_var_eta({17.28, 3.52, nanval}); - // Constant resistivity - const Teuchos::Array exp_ind_0_flux_const_eta( - {13.44, -7.04, nanval}); - const Teuchos::Array exp_ind_1_flux_const_eta( - {6.72, -3.52, nanval}); - - const auto exp_ind_0_flux = var_resistivity ? exp_ind_0_flux_var_eta - : exp_ind_0_flux_const_eta; - const auto exp_ind_1_flux = var_resistivity ? exp_ind_1_flux_var_eta - : exp_ind_1_flux_const_eta; - // NOTE: currently we will not have a flux vector for B_z - // (exp_ind_2_flux) as we are limiting the induced magnetic field to the - // mesh dimension. The fact that the reference script does compute non-zero - // expected values for the B_z flux suggests that perhaps we do need to - // always include the B_z component. - const Teuchos::Array exp_ind_2_flux({nanval, nanval, nanval}); - - Teuchos::ParameterList full_ind_params; - full_ind_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - if (build_magn_corr) - { - full_ind_params.set("Hyperbolic Divergence Cleaning Speed", 1.1); - } - full_ind_params.set("Variable Resistivity", var_resistivity); - if (!var_resistivity) - { - full_ind_params.set("Resistivity", 1.5); - } - Teuchos::ParameterList test_params; - test_params.set("Full Induction MHD Properties", full_ind_params); - test_params.set("Exp Ind 0 Flux", exp_ind_0_flux); - test_params.set("Exp Ind 1 Flux", exp_ind_1_flux); - test_params.set("Exp Ind 2 Flux", exp_ind_2_flux); - - testEval(test_params, "Foo_", "Bar_"); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxNoCleaning2D, residual_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval2D(false, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval2D(false, true); -} - -TEST(InductionResistiveFluxNoCleaning2D, jacobian_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval2D(false, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval2D(false, true); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxDivCleaning2D, residual_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval2D(true, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval2D(true, true); -} - -TEST(InductionResistiveFluxDivCleaning2D, jacobian_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval2D(true, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval2D(true, true); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxConstantResistivity2D, residual_test) -{ - testEval2D(false, false); -} - -TEST(InductionResistiveFluxConstantResistivity2D, jacobian_test) -{ - testEval2D(false, false); -} - -//-----------------------------------------------------------------// -template -void testEval3D(const bool build_magn_corr, const bool var_resistivity) -{ - // Expected values 3D - // Variable resistivity - const Teuchos::Array exp_ind_0_flux_var_eta({3.2, -20.48, -9.28}); - const Teuchos::Array exp_ind_1_flux_var_eta({17.28, 13.44, 49.92}); - const Teuchos::Array exp_ind_2_flux_var_eta({-4.16, -46.72, -3.2}); - // Constant resistivity - const Teuchos::Array exp_ind_0_flux_const_eta( - {-16.32, -7.04, 10.56}); - const Teuchos::Array exp_ind_1_flux_const_eta( - {6.72, -33.28, 20.16}); - const Teuchos::Array exp_ind_2_flux_const_eta({9.92, -19.84, 9.92}); - - const auto exp_ind_0_flux = var_resistivity ? exp_ind_0_flux_var_eta - : exp_ind_0_flux_const_eta; - const auto exp_ind_1_flux = var_resistivity ? exp_ind_1_flux_var_eta - : exp_ind_1_flux_const_eta; - const auto exp_ind_2_flux = var_resistivity ? exp_ind_2_flux_var_eta - : exp_ind_2_flux_const_eta; - - Teuchos::ParameterList full_ind_params; - full_ind_params.set("Build Magnetic Correction Potential Equation", - build_magn_corr); - if (build_magn_corr) - { - full_ind_params.set("Hyperbolic Divergence Cleaning Speed", 1.1); - } - full_ind_params.set("Variable Resistivity", var_resistivity); - if (!var_resistivity) - { - full_ind_params.set("Resistivity", 1.5); - } - Teuchos::ParameterList test_params; - test_params.set("Full Induction MHD Properties", full_ind_params); - test_params.set("Exp Ind 0 Flux", exp_ind_0_flux); - test_params.set("Exp Ind 1 Flux", exp_ind_1_flux); - test_params.set("Exp Ind 2 Flux", exp_ind_2_flux); - - testEval(test_params, "", ""); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxNoCleaning3D, residual_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval3D(false, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval3D(false, true); -} - -TEST(InductionResistiveFluxNoCleaning3D, jacobian_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval3D(false, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval3D(false, true); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxDivCleaning3D, residual_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval3D(true, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval3D(true, true); -} - -TEST(InductionResistiveFluxDivCleaning3D, jacobian_test) -{ - // For now, tests with variable resistivity will throw an error. - // Test for the appropriate error here, but retain the old test - // for use once a resistivity closure has been added. - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - testEval3D(true, true); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); - // testEval3D(true, true); -} - -//-----------------------------------------------------------------// -TEST(InductionResistiveFluxConstantResistivity3D, residual_test) -{ - testEval3D(false, false); -} - -TEST(InductionResistiveFluxConstantResistivity3D, jacobian_test) -{ - testEval3D(false, false); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.type_name = "InductionResistiveFlux"; - test_fixture.eval_name = "Induction Resistive Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::InductionResistiveFlux, - num_space_dim>(); -} - -TEST(InductionResistiveFlux_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(InductionResistiveFlux_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(InductionResistiveFlux_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(InductionResistiveFlux_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstMHDVortexProblemExact.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstMHDVortexProblemExact.cpp deleted file mode 100644 index 10c7aca..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstMHDVortexProblemExact.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MHDVortexProblemExact.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -void testEval() -{ - const int integration_order = 1; - const int basis_order = 1; - static constexpr int num_space_dim = NumSpaceDim; - - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - const double time = 0.6; - test_fixture.setTime(time); - - // Set non-trivial coordinates for the degree of freedom - const int num_point = ir.num_points; - auto ip_coord_view - = test_fixture.int_values->ip_coordinates.get_static_view(); - auto ip_coord_mirror = Kokkos::create_mirror(ip_coord_view); - ip_coord_mirror(0, 0, 0) = 0.2; - ip_coord_mirror(0, 0, 1) = 0.4; - Kokkos::deep_copy(ip_coord_view, ip_coord_mirror); - - // Closure parameters - Teuchos::Array xy_0(2); - xy_0[0] = 0.1; - xy_0[1] = -0.2; - Teuchos::Array vel_0(2); - vel_0[0] = 2.0; - vel_0[1] = -0.5; - Teuchos::ParameterList mhd_params; - mhd_params.set>("velocity_0", vel_0); - mhd_params.set>("center_0", xy_0); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::MHDVortexProblemExact(ir, mhd_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - test_fixture.registerTestField(eval->_induced_magnetic_field[0]); - test_fixture.registerTestField(eval->_induced_magnetic_field[1]); - test_fixture.registerTestField(eval->_velocity[0]); - test_fixture.registerTestField(eval->_velocity[1]); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_lag_pres - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto fv_mgn_field_0 = test_fixture.getTestFieldData( - eval->_induced_magnetic_field[0]); - const auto fv_mgn_field_1 = test_fixture.getTestFieldData( - eval->_induced_magnetic_field[1]); - const auto fv_vel_0 - = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto fv_vel_1 - = test_fixture.getTestFieldData(eval->_velocity[1]); - - // Assert values - const double tol = 1.0e-15; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_NEAR(1.9152034448503858, fieldValue(fv_lag_pres, 0, qp), tol); - EXPECT_NEAR( - -0.45120855259162973, fieldValue(fv_mgn_field_0, 0, qp), tol); - EXPECT_NEAR( - 0.07520142543193828, fieldValue(fv_mgn_field_1, 0, qp), tol); - EXPECT_NEAR(1.5487914474083704, fieldValue(fv_vel_0, 0, qp), tol); - EXPECT_NEAR(0.07520142543193828, fieldValue(fv_vel_1, 0, qp), tol); - - if (num_space_dim == 3) - { - const auto fv_mgn_field_2 = test_fixture.getTestFieldData( - eval->_induced_magnetic_field[2]); - const auto fv_vel_2 - = test_fixture.getTestFieldData(eval->_velocity[2]); - EXPECT_EQ(0.0, fieldValue(fv_mgn_field_2, 0, qp)); - EXPECT_EQ(0.0, fieldValue(fv_vel_2, 0, qp)); - } - } -} - -//-----------------------------------------------------------------// -TEST(MHDVortexProblemExact2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(MHDVortexProblemExact2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(MHDVortexProblemExact3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(MHDVortexProblemExact3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MHDVortexProblemExact"; - test_fixture.eval_name = "MHD Vortex Problem Exact Solution " - + std::to_string(num_space_dim) + "D."; - const Teuchos::Array dummy(2); - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("velocity_0", dummy) - .set("center_0", dummy); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.template buildAndTest< - ClosureModel::MHDVortexProblemExact, - num_space_dim>(); -} - -TEST(MHDVortexProblemExact_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MHDVortexProblemExact_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(MHDVortexProblemExact_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(MHDVortexProblemExact_Factory3D, jacobian_test) -{ - testFactory(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticCorrectionDampingSource.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticCorrectionDampingSource.cpp deleted file mode 100644 index f9b3ae8..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticCorrectionDampingSource.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticCorrectionDampingSource.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField - scalar_magnetic_potential; - - Dependencies(const panzer::IntegrationRule& ir) - : scalar_magnetic_potential("scalar_magnetic_potential", ir.dl_scalar) - { - this->addEvaluatedField(scalar_magnetic_potential); - - this->setName( - "Magnetic Correction Damping Source Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - scalar_magnetic_potential.deep_copy(0.3); - } -}; - -template -void testEval(const bool use_default_alpha) -{ - static constexpr int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - const double alpha = 2.0; - Teuchos::ParameterList full_induction_params; - full_induction_params.set("Build Magnetic Correction Potential Equation", - true); - if (use_default_alpha) - { - full_induction_params.set("Hyperbolic Divergence Cleaning Speed", - alpha * 0.18); - } - else - { - full_induction_params.set("Hyperbolic Divergence Cleaning Speed", 1.1); - full_induction_params.set("Magnetic Correction Damping Factor", alpha); - } - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - - // Initialize class object to test - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - auto eval = Teuchos::rcp( - new ClosureModel::MagneticCorrectionDampingSource( - ir, mhd_props)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_damping_potential_source); - - test_fixture.evaluate(); - - const auto src_magn_pot = test_fixture.getTestFieldData( - eval->_damping_potential_source); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(-0.6, fieldValue(src_magn_pot, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(MagneticCorrectionDampingSource, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(MagneticCorrectionDampingSource, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(MagneticCorrectionDampingSourceDefaultAlpha, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(MagneticCorrectionDampingSourceDefaultAlpha, jacobian_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.1) - .set("Build Magnetic Correction Potential Equation", false) - .set("Hyperbolic Divergence Cleaning Speed", 1.0); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.type_name = "MagneticCorrectionDampingSource"; - test_fixture.eval_name = "Magnetic Correction Damping Source"; - test_fixture.template buildAndTest< - ClosureModel::MagneticCorrectionDampingSource, - num_space_dim>(); -} - -TEST(MagneticCorrectionDampingSource_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MagneticCorrectionDampingSource_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(MagneticCorrectionDampingSource_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(MagneticCorrectionDampingSource_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticPressure.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticPressure.cpp deleted file mode 100644 index 0621bad..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstMagneticPressure.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_MagneticPressure.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - Kokkos::Array, 3> - tot_magn_field; - - const Kokkos::Array _b; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array b) - : _b(b) - { - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, tot_magn_field, "total_magnetic_field_"); - this->setName("Magnetic Pressure"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - const int num_field_dim = tot_magn_field.size(); - for (int dim = 0; dim < num_field_dim; ++dim) - tot_magn_field[dim].deep_copy(_b[dim]); - } -}; - -template -void testEval(const int num_space_dim) -{ - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize magnetic field components and dependencies - const double b0 = 0.25; - const double b1 = 0.5; - const double b2 = num_space_dim == 3 ? 0.75 : 0.125; - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, {b0, b1, b2})); - test_fixture.registerEvaluator(deps); - - // Closure parameters - const double mu_0 = 2.0e-3; - Teuchos::ParameterList full_induction_params; - full_induction_params.set("Vacuum Magnetic Permeability", mu_0); - MHDProperties::FullInductionMHDProperties mhd_props - = MHDProperties::FullInductionMHDProperties(full_induction_params); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::MagneticPressure( - ir, mhd_props)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_magnetic_pressure); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_magn_pres - = test_fixture.getTestFieldData(eval->_magnetic_pressure); - - // Expected values - const int num_point = ir.num_points; - const double exp_magn_pres = (b0 * b0 + b1 * b1 + b2 * b2) / (2.0 * mu_0); - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_magn_pres, fieldValue(fv_magn_pres, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(MagneticPressure2D, residual_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(MagneticPressure2D, jacobian_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(MagneticPressure3D, residual_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -TEST(MagneticPressure3D, jacobian_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "MagneticPressure"; - test_fixture.eval_name = "Magnetic Pressure"; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.125); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.template buildAndTest< - ClosureModel::MagneticPressure, - num_space_dim>(); -} - -TEST(MagneticPressure_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(MagneticPressure_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(MagneticPressure_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(MagneticPressure_Factory3D, jacobian_test) -{ - testFactory(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstResistivityFactory.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstResistivityFactory.cpp deleted file mode 100644 index 8633bba..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstResistivityFactory.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include "closure_models/VertexCFD_Closure_ConstantScalarField.hpp" - -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ - -//-----------------------------------------------------------------// -// Full Induction MHD closure model factory test for "Resistivity" -// using the constant scalar field closure model -//-----------------------------------------------------------------// -template -void testFactory(const bool variable_resistivity) -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "Resistivity"; - test_fixture.eval_name = "Constant Scalar Field \"resistivity\""; - test_fixture.user_params.sublist("Full Induction MHD Properties") - .set("Vacuum Magnetic Permeability", 0.125) - .set("Build Resistive Flux", true) - .set("Resistivity", 1.25) - .set("Variable Resistivity", variable_resistivity); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.template buildAndTest< - ClosureModel::ConstantScalarField, - num_space_dim>(); -} - -TEST(ResistivityFactory2D, residual_test) -{ - testFactory(false); -} - -TEST(ResistivityFactory2D, jacobian_test) -{ - testFactory(false); -} - -TEST(ResistivityFactory3D, residual_test) -{ - testFactory(false); -} - -TEST(ResistivityFactory3D, jacobian_test) -{ - testFactory(false); -} - -//-----------------------------------------------------------------// -// Full Induction MHD closure model factory exception test for -// "Resistivity" when "Variable Resistivity" is true. -//-----------------------------------------------------------------// -template -void testFactoryException() -{ - const std::string msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - (testFactory(true)); - } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -TEST(ResistivityFactoryException, residual_test) -{ - testFactoryException(); -} - -TEST(ResistivityFactoryException, jacobian_test) -{ - testFactoryException(); -} - -//-----------------------------------------------------------------// - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticField.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticField.cpp deleted file mode 100644 index 7ae9372..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticField.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticField.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - Kokkos::Array, 3> - ind_magn_field; - Kokkos::Array, 3> - ext_magn_field; - - const Kokkos::Array _bi; - const Kokkos::Array _b0; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array bi, - const Kokkos::Array b0, - const std::string& field_prefix) - : _bi(bi) - , _b0(b0) - { - Utils::addEvaluatedVectorField( - *this, - ir.dl_scalar, - ind_magn_field, - field_prefix + "induced_magnetic_field_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, ext_magn_field, "external_magnetic_field_"); - this->setName("Total Magnetic Field Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - for (int dim = 0; dim < 3; ++dim) - { - ind_magn_field[dim].deep_copy(_bi[dim]); - ext_magn_field[dim].deep_copy(_b0[dim]); - } - } -}; - -template -void testEval(const std::string& field_prefix) -{ - static constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize induced / external magnetic field components and - // dependencies - const Kokkos::Array bi - = {0.25, - 0.5, - num_space_dim > 2 ? 0.9 : std::numeric_limits::quiet_NaN()}; - const Kokkos::Array b0 = {0.325, -0.65, 0.7}; - const Kokkos::Array b_exp - = {bi[0] + b0[0], - bi[1] + b0[1], - num_space_dim == 2 ? b0[2] : b0[2] + bi[2]}; - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, bi, b0, field_prefix)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - const auto eval = [&]() { - if (field_prefix == "") - { - // check evaluator with defaulted field name - return Teuchos::rcp( - new ClosureModel::TotalMagneticField(ir)); - } - else - { - // check evaluator with specified field name prefix - return Teuchos::rcp( - new ClosureModel:: - TotalMagneticField( - ir, field_prefix)); - } - }(); - - test_fixture.registerEvaluator(eval); - - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - eval->_total_magnetic_field[dim]); - } - test_fixture.evaluate(); - - // Evaluate test fields - const auto bt_0 = test_fixture.getTestFieldData( - eval->_total_magnetic_field[0]); - const auto bt_1 = test_fixture.getTestFieldData( - eval->_total_magnetic_field[1]); - const auto bt_2 = test_fixture.getTestFieldData( - eval->_total_magnetic_field[2]); - - // Expected values - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(b_exp[0], fieldValue(bt_0, 0, qp)); - EXPECT_EQ(b_exp[1], fieldValue(bt_1, 0, qp)); - EXPECT_EQ(b_exp[2], fieldValue(bt_2, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticField2D, residual_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticField2D, jacobian_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticField3D, residual_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticField3D, jacobian_test) -{ - testEval(""); -} -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldWithFieldPrefix3D, residual_test) -{ - testEval("FOO_"); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldWithFieldPrefix3D, jacobian_test) -{ - testEval("FOO_"); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - // The factory registers this closure and TotalMagneticFieldGradient for - // type_name = "TotalMagneticField". This closure model will be the first - // of the two registered evals - test_fixture.num_evaluators = 2; - test_fixture.eval_index = 0; - test_fixture.type_name = "TotalMagneticField"; - test_fixture.eval_name = "Total Magnetic Field " - + std::to_string(num_space_dim) + "D"; - test_fixture.user_params.sublist("Full Induction MHD Properties"); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.template buildAndTest< - ClosureModel::TotalMagneticField, - num_space_dim>(); -} - -TEST(TotalMagneticField_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(TotalMagneticField_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(TotalMagneticField_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(TotalMagneticField_Factory3D, jacobian_test) -{ - testFactory(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticFieldGradient.cpp b/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticFieldGradient.cpp deleted file mode 100644 index 67e4f9e..0000000 --- a/src/full_induction_mhd_solver/closure_models/unit_test/tstTotalMagneticFieldGradient.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include -#include - -#include "full_induction_mhd_solver/closure_models/VertexCFD_Closure_TotalMagneticFieldGradient.hpp" - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - Kokkos::Array< - PHX::MDField, - 3> - grad_ind_magn_field; - Kokkos::Array< - PHX::MDField, - 3> - grad_ext_magn_field; - - const Kokkos::Array _bi; - const Kokkos::Array _b0; - - Dependencies(const panzer::IntegrationRule& ir, - const Kokkos::Array bi, - const Kokkos::Array b0, - const std::string& gradient_prefix) - : _bi(bi) - , _b0(b0) - { - Utils::addEvaluatedVectorField( - *this, - ir.dl_vector, - grad_ind_magn_field, - gradient_prefix + "GRAD_induced_magnetic_field_"); - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - grad_ext_magn_field, - "GRAD_external_magnetic_field_"); - this->setName("Total Magnetic Field Gradient Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "total magnetic field gradient test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_ind_magn_field[0].extent(1); - const int num_grad_dim = grad_ind_magn_field[0].extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < 3; ++dim) - { - const double qpd = (qp + dim + 1); - for (int grad_dim = 0; grad_dim < num_grad_dim; ++grad_dim) - { - const double mult = qpd / (grad_dim + 1); - grad_ind_magn_field[dim](c, qp, grad_dim) = _bi[dim] * mult; - grad_ext_magn_field[dim](c, qp, grad_dim) = _b0[dim] * mult; - } - } - } - } -}; - -template -void testEval(const std::string& gradient_prefix) -{ - static constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize induced / external magnetic field gradient base values - const Kokkos::Array bi - = {0.25, - 0.5, - num_space_dim > 2 ? 0.9 : std::numeric_limits::quiet_NaN()}; - const Kokkos::Array b0 = {0.325, -0.65, 0.7}; - - // Eval dependencies - const auto deps = Teuchos::rcp( - new Dependencies(ir, bi, b0, gradient_prefix)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - const auto eval = [&]() { - if (gradient_prefix == "") - { - // check evaluator with defaulted field name - return Teuchos::rcp( - new ClosureModel::TotalMagneticFieldGradient(ir)); - } - else - { - // check evaluator with specified field name prefix - return Teuchos::rcp( - new ClosureModel::TotalMagneticFieldGradient( - ir, gradient_prefix)); - } - }(); - - test_fixture.registerEvaluator(eval); - - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - eval->_grad_total_magnetic_field[dim]); - } - test_fixture.evaluate(); - - // Evaluate test fields - const auto grad_bt_0 = test_fixture.getTestFieldData( - eval->_grad_total_magnetic_field[0]); - const auto grad_bt_1 = test_fixture.getTestFieldData( - eval->_grad_total_magnetic_field[1]); - const auto grad_bt_2 = test_fixture.getTestFieldData( - eval->_grad_total_magnetic_field[2]); - - // Expected values - const int num_point = ir.num_points; - const int num_grad_dim = ir.spatial_dimension; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_grad_dim; ++dim) - { - // Currently force assumption of uniform external field, so the - // expected total field gradient is just the induced field gradient - const double exp_0 = bi[0] * (qp + 1) / (dim + 1); - EXPECT_DOUBLE_EQ(exp_0, fieldValue(grad_bt_0, 0, qp, dim)); - const double exp_1 = bi[1] * (qp + 2) / (dim + 1); - EXPECT_DOUBLE_EQ(exp_1, fieldValue(grad_bt_1, 0, qp, dim)); - const double exp_2 - = num_space_dim < 3 ? 0.0 : bi[2] * (qp + 3) / (dim + 1); - EXPECT_DOUBLE_EQ(exp_2, fieldValue(grad_bt_2, 0, qp, dim)); - } - } -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradient2D, residual_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradient2D, jacobian_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradient3D, residual_test) -{ - testEval(""); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradient3D, jacobian_test) -{ - testEval(""); -} -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradientWithFieldPrefix3D, residual_test) -{ - testEval("FOO_"); -} - -//-----------------------------------------------------------------// -TEST(TotalMagneticFieldGradientWithFieldPrefix3D, jacobian_test) -{ - testEval("FOO_"); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - static constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - // Gradient closure is built with "TotalMagneticField" in the factory, and - // will be the second of the two evaluators - test_fixture.num_evaluators = 2; - test_fixture.eval_index = 1; - test_fixture.type_name = "TotalMagneticField"; - test_fixture.eval_name = "Total Magnetic Field Gradient" - + std::to_string(num_space_dim) + "D"; - test_fixture.user_params.sublist("Full Induction MHD Properties"); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 1.5) - .set("Artificial compressibility", 0.1); - test_fixture.template buildAndTest< - ClosureModel::TotalMagneticFieldGradient, - num_space_dim>(); -} - -TEST(TotalMagneticFieldGradient_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(TotalMagneticFieldGradient_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(TotalMagneticFieldGradient_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(TotalMagneticFieldGradient_Factory3D, jacobian_test) -{ - testFactory(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.cpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.cpp deleted file mode 100644 index 26ed5a2..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_FullInductionInitialConditionFactory.hpp" -#include "VertexCFD_FullInductionInitialConditionFactory_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM( - VertexCFD::InitialCondition::FullInductionICFactory) diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp deleted file mode 100644 index 70dbe48..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_HPP -#define VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_HPP - -#include - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class FullInductionICFactory -{ - public: - static constexpr int num_space_dim = NumSpaceDim; - - void buildClosureModel( - const std::string& closure_type, - const std::vector>& bases, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& ic_params, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators); -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory_impl.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory_impl.hpp deleted file mode 100644 index 78f99e7..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory_impl.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_IMPL_HPP -#define VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_IMPL_HPP - -#include "VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp" -#include "VertexCFD_InitialCondition_MHDVortexProblem.hpp" - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -void FullInductionICFactory::buildClosureModel( - const std::string& ic_type, - const std::vector>& bases, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& /*ic_params*/, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators) -{ - const auto mhd_prop_list - = user_params.sublist("Full Induction MHD Properties"); - - if (ic_type == "MHDVortexProblem") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new MHDVortexProblem( - mhd_prop_list, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (ic_type == "DivergenceAdvectionTest") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new DivergenceAdvectionTest( - mhd_prop_list, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - error_msg = "DivergenceAdvectionTest\n"; - error_msg += "MHDVortexProblem\n"; -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_FULLINDUCTIONINITIALCONDITIONFACTORY_IMPL_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.cpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.cpp deleted file mode 100644 index 1303e45..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp" -#include "VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::DivergenceAdvectionTest) diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp deleted file mode 100644 index 16bdd3f..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_HPP -#define VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -template -class DivergenceAdvectionTest : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - DivergenceAdvectionTest(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, - num_space_dim> - _induced_magnetic_field; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; - - double _phi; - double _r0; - Kokkos::Array _xy_0; - Kokkos::Array _vel; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#include "VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp" - -#endif // end VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp deleted file mode 100644 index 718c179..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest_impl.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_IMPL_HPP - -#include -#include -#include -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -DivergenceAdvectionTest::DivergenceAdvectionTest( - const Teuchos::ParameterList& mhd_params, const panzer::PureBasis& basis) - : _lagrange_pressure("lagrange_pressure", basis.functional) - , _basis_name(basis.name()) - , _phi(6.0) - , _r0(1.0 / std::sqrt(8.0)) - , _xy_0({0.0, 0.0, 0.0}) - , _vel({1.0, 1.0, 0.0}) -{ - Teuchos::ParameterList params; - if (mhd_params.isSublist("Divergence Advection Test")) - { - params = mhd_params.sublist("Divergence Advection Test"); - } - - if (params.isType("Lagrange Pressure")) - { - _phi = params.get("Lagrange Pressure"); - } - - if (params.isType("Divergence Bubble Radius")) - { - _r0 = params.get("Divergence Bubble Radius"); - } - - if (params.isType>("Divergence Bubble Center")) - { - const auto xy_0 - = params.get>("Divergence Bubble Center"); - for (int dim = 0; dim < num_space_dim; ++dim) - { - _xy_0[dim] = xy_0[dim]; - } - } - - if (params.isType>("Velocity")) - { - const auto vel = params.get>("Velocity"); - for (int dim = 0; dim < num_space_dim; ++dim) - { - _vel[dim] = vel[dim]; - } - } - - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - - Utils::addEvaluatedVectorField( - *this, basis.functional, _velocity, "velocity_", true); - - Utils::addEvaluatedVectorField(*this, - basis.functional, - _induced_magnetic_field, - "induced_magnetic_field_", - true); - - this->setName("DivergenceAdvectionTest " + std::to_string(num_space_dim) - + "D Initial Condition"); -} - -//---------------------------------------------------------------------------// -template -void DivergenceAdvectionTest::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void DivergenceAdvectionTest::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void DivergenceAdvectionTest::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _lagrange_pressure.extent(1); - using Constants::pi; - using std::pow; - using std::sqrt; - - const double b_coeff = 1.0 / sqrt(4.0 * pi); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - const double x = _basis_coords(cell, basis, 0); - const double y = _basis_coords(cell, basis, 1); - const double dx = x - _xy_0[0]; - const double dy = y - _xy_0[1]; - const double r = sqrt(dx * dx + dy * dy); - - _lagrange_pressure(cell, basis) = _phi; - - if (r < _r0) - { - _induced_magnetic_field[0](cell, basis) - = b_coeff * (pow(r / _r0, 8) - 2.0 * pow(r / _r0, 4) + 1); - } - else - { - _induced_magnetic_field[0](cell, basis) = 0.0; - } - _induced_magnetic_field[1](cell, basis) = 0.0; - - _velocity[0](cell, basis) = _vel[0]; - _velocity[1](cell, basis) = _vel[1]; - - if (num_space_dim > 2) - { - // For now, set induced B_z for 3D cases to zero, so that for - // both 2D and 3D we do the same thing (2D has no induced Bz - // so we need to use the ExternalMagneticField closure in that - // case until / unless we switch to always solving B_z - // independent of mesh dimension) - _induced_magnetic_field[2](cell, basis) = 0.0; - _velocity[2](cell, basis) = _vel[2]; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_DIVERGENCEADVECTIONTEST_IMPL_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.cpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.cpp deleted file mode 100644 index 84759ee..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_MHDVortexProblem.hpp" -#include "VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::MHDVortexProblem) diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp deleted file mode 100644 index 656b900..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_HPP -#define VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -template -class MHDVortexProblem : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - MHDVortexProblem(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, - num_space_dim> - _induced_magnetic_field; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; - - Kokkos::Array _vel_0; - Kokkos::Array _xy_0; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#include "VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp" - -#endif // end VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp b/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp deleted file mode 100644 index 7098769..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem_impl.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_IMPL_HPP - -#include -#include -#include -#include -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -MHDVortexProblem::MHDVortexProblem( - const Teuchos::ParameterList& params, const panzer::PureBasis& basis) - : _lagrange_pressure("lagrange_pressure", basis.functional) - , _basis_name(basis.name()) -{ - const auto vel_0 = params.get>("velocity_0"); - const auto xy_0 = params.get>("center_0"); - for (int dim = 0; dim < 2; ++dim) - { - _vel_0[dim] = vel_0[dim]; - _xy_0[dim] = xy_0[dim]; - } - - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - - Utils::addEvaluatedVectorField( - *this, basis.functional, _velocity, "velocity_", true); - - Utils::addEvaluatedVectorField(*this, - basis.functional, - _induced_magnetic_field, - "induced_magnetic_field_", - true); - - this->setName("MHDVortexProblem " + std::to_string(num_space_dim) - + "D Initial Condition"); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblem::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblem::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MHDVortexProblem::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _lagrange_pressure.extent(1); - using std::exp; - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - const double x = _basis_coords(cell, basis, 0); - const double y = _basis_coords(cell, basis, 1); - const double r2 = (x - _xy_0[0]) * (x - _xy_0[0]) - + (y - _xy_0[1]) * (y - _xy_0[1]); - - _lagrange_pressure(cell, basis) - = 1.0 + 0.5 * exp(1.) * (1. - r2 * exp(-r2)); - - _induced_magnetic_field[0](cell, basis) = exp(0.5 * (1.0 - r2)) - * (_xy_0[1] - y); - _induced_magnetic_field[1](cell, basis) = exp(0.5 * (1.0 - r2)) - * (x - _xy_0[0]); - - _velocity[0](cell, basis) = _induced_magnetic_field[0](cell, basis) - + _vel_0[0]; - _velocity[1](cell, basis) = _induced_magnetic_field[1](cell, basis) - + _vel_0[1]; - if (num_space_dim > 2) - { - _induced_magnetic_field[2](cell, basis) = 0.0; - _velocity[2](cell, basis) = 0.0; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_MHDVORTEXPROBLEM_IMPL_HPP diff --git a/src/full_induction_mhd_solver/initial_conditions/unit_test/CMakeLists.txt b/src/full_induction_mhd_solver/initial_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index 621ea7e..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - MHDVortexProblem - DivergenceAdvectionTest - ) diff --git a/src/full_induction_mhd_solver/initial_conditions/unit_test/tstDivergenceAdvectionTest.cpp b/src/full_induction_mhd_solver/initial_conditions/unit_test/tstDivergenceAdvectionTest.cpp deleted file mode 100644 index a8be139..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/unit_test/tstDivergenceAdvectionTest.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include - -#include "full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_DivergenceAdvectionTest.hpp" - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------//¬ -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -auto compute_ref_sol(const Teuchos::ParameterList& params, const SubviewType& x) -{ - using Constants::pi; - using std::pow; - using std::sqrt; - - constexpr int num_conserve = 6; - Kokkos::Array ref_solution; - - const auto psi = params.isType("Lagrange Pressure") - ? params.get("Lagrange Pressure") - : 6.0; - - const auto r0 = params.isType("Divergence Bubble Radius") - ? params.get("Divergence Bubble Radius") - : 1.0 / sqrt(8.0); - - const auto x_c - = params.isType>("Divergence Bubble Center") - ? params.get>("Divergence Bubble Center") - : Teuchos::Array(3, 0); - - const auto vel = params.isType>("Velocity") - ? params.get>("Velocity") - : Teuchos::Array({1.0, 1.0, 0.0}); - - const double dx = x[0] - x_c[0]; - const double dy = x[1] - x_c[1]; - const double r = sqrt(dx * dx + dy * dy); - - // Lagrange pressure - ref_solution[0] = psi; - // Magnetic field - if (r < r0) - { - ref_solution[1] = (pow(r / r0, 8) - 2.0 * pow(r / r0, 4) + 1) - / sqrt(4.0 * pi); - } - else - { - ref_solution[1] = 0.0; - } - ref_solution[2] = 0.0; - // Velocity - ref_solution[3] = vel[0]; - ref_solution[4] = vel[1]; - ref_solution[5] = vel[2]; - - return ref_solution; -} - -//---------------------------------------------------------------------------// -template -void testEval(const bool set_input_params = false) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const double r0 = 1.5; - const double nanval = std::numeric_limits::signaling_NaN(); - const double x_z = num_space_dim == 2 ? nanval : 0.5; - const Teuchos::Array x0({0.3, 0.4, x_z}); - Teuchos::ParameterList model_params; - if (set_input_params) - { - model_params.set("Lagrange Pressure", 2.3); - model_params.set("Divergence Bubble Radius", r0); - model_params.set("Divergence Bubble Center", x0); - const double v_z = num_space_dim == 2 ? nanval : 0.325; - model_params.set("Velocity", - Teuchos::Array({0.2, -0.125, v_z})); - } - Teuchos::ParameterList mhd_params; - mhd_params.set("Divergence Advection Test", model_params); - - const int num_coord = test_fixture.cell_topo->getNodeCount(); - - // Set non-trivial coordinates for the degree of freedom - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - for (int basis = 0; basis < num_coord; basis++) - { - // set coordinates inside and outside of bubble radius - x(0, basis) = r0 / (num_coord - 2) * basis * std::cos(basis) + x0[0]; - x(1, basis) = r0 / (num_coord - 2) * basis * std::sin(basis) + x0[1]; - if (num_space_dim > 2) - x(2, basis) = r0 * (basis); - for (int dim = 0; dim < num_space_dim; ++dim) - { - basis_coord_mirror(0, basis, dim) = x(dim, basis); - } - } - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - // Create evaluator. - auto mhd_val = Teuchos::rcp( - new InitialCondition:: - DivergenceAdvectionTest( - mhd_params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(mhd_val); - - // Add required test fields. - test_fixture.registerTestField(mhd_val->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(mhd_val->_velocity[dim]); - test_fixture.registerTestField( - mhd_val->_induced_magnetic_field[dim]); - } - - // Evaluate IC. - test_fixture.evaluate(); - - // Check the IC value for all scalars. - const auto lg_pressure_result - = test_fixture.getTestFieldData(mhd_val->_lagrange_pressure); - const auto velocity_0_result - = test_fixture.getTestFieldData(mhd_val->_velocity[0]); - const auto velocity_1_result - = test_fixture.getTestFieldData(mhd_val->_velocity[1]); - const auto magn_field_0_result = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[0]); - const auto magn_field_1_result = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[1]); - - // Assert number of points - EXPECT_EQ(num_coord, lg_pressure_result.extent(1)); - EXPECT_EQ(num_coord, velocity_0_result.extent(1)); - EXPECT_EQ(num_coord, velocity_1_result.extent(1)); - EXPECT_EQ(num_coord, magn_field_0_result.extent(1)); - EXPECT_EQ(num_coord, magn_field_1_result.extent(1)); - - // Loop over number of points and compare against reference values - const int num_point = lg_pressure_result.extent(1); - const double tol = 1.0e-15; - for (int qp = 0; qp < num_point; ++qp) - { - const auto x_subview = Kokkos::subview(x, Kokkos::ALL(), qp); - - // Compute reference values - const auto ref_solution = compute_ref_sol(model_params, x_subview); - - // Assert - EXPECT_NEAR( - ref_solution[0], fieldValue(lg_pressure_result, 0, qp), tol); - EXPECT_NEAR( - ref_solution[1], fieldValue(magn_field_0_result, 0, qp), tol); - EXPECT_NEAR( - ref_solution[2], fieldValue(magn_field_1_result, 0, qp), tol); - EXPECT_DOUBLE_EQ(ref_solution[3], fieldValue(velocity_0_result, 0, qp)); - EXPECT_DOUBLE_EQ(ref_solution[4], fieldValue(velocity_1_result, 0, qp)); - - if (num_space_dim == 3) - { - const auto velocity_2_result - = test_fixture.getTestFieldData( - mhd_val->_velocity[2]); - EXPECT_EQ(num_coord, velocity_2_result.extent(1)); - EXPECT_DOUBLE_EQ(ref_solution[5], - fieldValue(velocity_2_result, 0, qp)); - - const auto magn_field_2_result - = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[2]); - EXPECT_EQ(num_coord, magn_field_2_result.extent(1)); - EXPECT_DOUBLE_EQ(0.0, fieldValue(magn_field_2_result, 0, qp)); - } - } -} - -//---------------------------------------------------------------------------// -// Divergence Advection Test 2D, default values -// Residual -TEST(DivergenceAdvectionTestDefaultIC2D, residual) -{ - testEval(); -} - -// Jacobian -TEST(DivergenceAdvectionTestDefaultIC2D, jacobian) -{ - testEval(); -} - -// Divergence Advection Test 2D, input values -// Residual -TEST(DivergenceAdvectionTestInputsIC2D, residual) -{ - testEval(true); -} - -// Jacobian -TEST(DivergenceAdvectionTestInputsIC2D, jacobian) -{ - testEval(true); -} - -// Divergence Advection Test 3D, default values -// Residual -TEST(DivergenceAdvectionTestDefaultIC3D, residual) -{ - testEval(); -} - -// Jacobian -TEST(DivergenceAdvectionTestDefaultIC3D, jacobian) -{ - testEval(); -} - -// Divergence Advection Test 3D, input values -// Residual -TEST(DivergenceAdvectionTestInputsIC3D, residual) -{ - testEval(true); -} - -// Jacobian -TEST(DivergenceAdvectionTestInputsIC3D, jacobian) -{ - testEval(true); -} -//---------------------------------------------------------------------------// - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/initial_conditions/unit_test/tstMHDVortexProblem.cpp b/src/full_induction_mhd_solver/initial_conditions/unit_test/tstMHDVortexProblem.cpp deleted file mode 100644 index a201805..0000000 --- a/src/full_induction_mhd_solver/initial_conditions/unit_test/tstMHDVortexProblem.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include - -#include "full_induction_mhd_solver/initial_conditions/VertexCFD_InitialCondition_MHDVortexProblem.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------//¬ -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -auto compute_ref_sol(const Teuchos::ParameterList& params, const SubviewType& x) -{ - using std::exp; - using std::sqrt; - - constexpr int num_conserve = 5; - Kokkos::Array ref_solution; - - // Get MHD vortex problem properties - const auto vel_0 = params.get>("velocity_0"); - const auto xy_0 = params.get>("center_0"); - - const double dx = x[0] - xy_0[0]; - const double dy = x[1] - xy_0[1]; - const double r2 = dx * dx + dy * dy; - - // Lagrange pressure - ref_solution[0] = 1.0 + 0.5 * exp(1.) * (1. - r2 * exp(-r2)); - // Magnetic field - ref_solution[1] = -exp(0.5 * (1.0 - r2)) * dy; - ref_solution[2] = exp(0.5 * (1.0 - r2)) * dx; - // Velocity - ref_solution[3] = ref_solution[1] + vel_0[0]; - ref_solution[4] = ref_solution[2] + vel_0[1]; - - return ref_solution; -} - -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const int num_coord = test_fixture.cell_topo->getNodeCount(); - - // Set non-trivial coordinates for the degree of freedom - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - Kokkos::Array _xy_0{}; - for (int dim = 0; dim < num_space_dim; dim++) - { - for (int basis = 0; basis < num_coord; basis++) - { - // random coordinate assigned - x(dim, basis) = (0.5 + dim * 4.5) * (basis + 1); - basis_coord_mirror(0, basis, dim) = x(dim, basis); - _xy_0[dim] += x(dim, basis); - } - } - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList model_params; - Teuchos::Array xy_0(2); - Teuchos::Array vel_0(2); - vel_0[0] = 1.2; - vel_0[1] = 2.4; - - // center of vortex is center of the cell - for (int dim = 0; dim < 2; ++dim) - xy_0[dim] = _xy_0[dim] / std::pow(num_space_dim, 2); - - model_params.set>("velocity_0", vel_0); - model_params.set>("center_0", xy_0); - - // Create evaluator. - auto mhd_val = Teuchos::rcp( - new InitialCondition:: - MHDVortexProblem( - model_params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(mhd_val); - - // Add required test fields. - test_fixture.registerTestField(mhd_val->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(mhd_val->_velocity[dim]); - test_fixture.registerTestField( - mhd_val->_induced_magnetic_field[dim]); - } - - // Evaluate IC. - test_fixture.evaluate(); - - // Check the IC value for all scalars. - const auto lg_pressure_result - = test_fixture.getTestFieldData(mhd_val->_lagrange_pressure); - const auto velocity_0_result - = test_fixture.getTestFieldData(mhd_val->_velocity[0]); - const auto velocity_1_result - = test_fixture.getTestFieldData(mhd_val->_velocity[1]); - const auto magn_field_0_result = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[0]); - const auto magn_field_1_result = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[1]); - - // Assert number of points - EXPECT_EQ(num_coord, lg_pressure_result.extent(1)); - EXPECT_EQ(num_coord, velocity_0_result.extent(1)); - EXPECT_EQ(num_coord, velocity_1_result.extent(1)); - EXPECT_EQ(num_coord, magn_field_0_result.extent(1)); - EXPECT_EQ(num_coord, magn_field_1_result.extent(1)); - - // Loop over number of points and compare against reference values - const int num_point = lg_pressure_result.extent(1); - const double tol = 1.0e-15; - for (int d = 0; d < num_point; ++d) - { - const auto x_subview = Kokkos::subview(x, Kokkos::ALL(), d); - - // Compute reference values - const auto ref_solution = compute_ref_sol(model_params, x_subview); - - // Assert - EXPECT_NEAR(ref_solution[0], fieldValue(lg_pressure_result, 0, d), tol); - EXPECT_NEAR( - ref_solution[1], fieldValue(magn_field_0_result, 0, d), tol); - EXPECT_NEAR( - ref_solution[2], fieldValue(magn_field_1_result, 0, d), tol); - EXPECT_DOUBLE_EQ(ref_solution[3], fieldValue(velocity_0_result, 0, d)); - EXPECT_DOUBLE_EQ(ref_solution[4], fieldValue(velocity_1_result, 0, d)); - - if (num_space_dim == 3) - { - const auto velocity_2_result - = test_fixture.getTestFieldData( - mhd_val->_velocity[2]); - EXPECT_EQ(num_coord, velocity_2_result.extent(1)); - EXPECT_DOUBLE_EQ(0.0, fieldValue(velocity_2_result, 0, d)); - - const auto magn_field_2_result - = test_fixture.getTestFieldData( - mhd_val->_induced_magnetic_field[2]); - EXPECT_EQ(num_coord, magn_field_2_result.extent(1)); - EXPECT_DOUBLE_EQ(0.0, fieldValue(magn_field_2_result, 0, d)); - } - } -} - -//---------------------------------------------------------------------------// -// MHD vortex probem 2D -// Residual -TEST(MHDVortexProblemIC2D, residual) -{ - testEval(); -} - -// Jacobian -TEST(MHDVortexProblemIC2D, jacobian) -{ - testEval(); -} - -// MHD vortex probem 3D -// Residual -TEST(MHDVortexProblemIC3D, residual) -{ - testEval(); -} - -// Jacobian -TEST(MHDVortexProblemIC3D, jacobian) -{ - testEval(); -} -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp b/src/full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp deleted file mode 100644 index 402ddab..0000000 --- a/src/full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef VERTEXCFD_FULLINDUCTIONMHDPROPERTIES_HPP -#define VERTEXCFD_FULLINDUCTIONMHDPROPERTIES_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace MHDProperties -{ -//---------------------------------------------------------------------------// -// Full induction MHD properties -//---------------------------------------------------------------------------// - -class FullInductionMHDProperties -{ - public: - FullInductionMHDProperties() = default; - explicit FullInductionMHDProperties(const Teuchos::ParameterList& mhd_params) - : _build_magn_corr(false) - , _build_resistive_flux(false) - , _variable_resistivity(false) - , _mu_0(1.0) - , _eta(std::numeric_limits::quiet_NaN()) - , _c_h(0.0) - { - if (mhd_params.isType("Build Magnetic Correction " - "Potential Equation")) - { - _build_magn_corr = mhd_params.get( - "Build Magnetic " - "Correction Potential " - "Equation"); - } - - // Vacuum magnetic permeability - if (mhd_params.isType("Vacuum Magnetic Permeability")) - { - _mu_0 = mhd_params.get("Vacuum Magnetic Permeability"); - } - - // Resistivity - if (mhd_params.isType("Build Resistive Flux")) - { - _build_resistive_flux - = mhd_params.get("Build Resistive Flux"); - } - - if (_build_resistive_flux) - { - if (mhd_params.isType("Variable Resistivity")) - { - _variable_resistivity - = mhd_params.get("Variable Resistivity"); - } - if (_variable_resistivity) - { - throw std::runtime_error( - "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."); - } - else - { - _eta = mhd_params.get("Resistivity"); - } - } - - // Divergence cleaning parameters - // Hyperbolic divergence cleaning speed - if (_build_magn_corr) - { - _c_h = mhd_params.get( - "Hyperbolic Divergence Cleaning Speed"); - } - // Magnetic correction potential equaiton damping factor - if (mhd_params.isType("Magnetic Correction Damping Factor")) - { - _alpha - = mhd_params.get("Magnetic Correction Damping Factor"); - } - else - { - _alpha = _c_h / 0.18; - } - } - - // Build magnetic correction boolean - KOKKOS_INLINE_FUNCTION bool buildMagnCorr() const - { - return _build_magn_corr; - } - - // Build magnetic correction boolean - KOKKOS_INLINE_FUNCTION bool buildResistiveFlux() const - { - return _build_resistive_flux; - } - - // Variable resistivity boolean - KOKKOS_INLINE_FUNCTION bool variableResistivity() const - { - return _variable_resistivity; - } - - // Vacuum magnetic permeability - KOKKOS_INLINE_FUNCTION double vacuumMagneticPermeability() const - { - return _mu_0; - } - - // Constant resistivity - KOKKOS_INLINE_FUNCTION double resistivity() const { return _eta; } - - // Divergence cleaning speed - KOKKOS_INLINE_FUNCTION double hyperbolicDivergenceCleaningSpeed() const - { - return _c_h; - } - - // Magnetic correction damping factor - KOKKOS_INLINE_FUNCTION double magneticCorrectionDampingFactor() const - { - return _alpha; - } - - private: - bool _build_magn_corr; - bool _build_resistive_flux; - bool _variable_resistivity; - double _mu_0; - double _eta; - double _c_h; - double _alpha; -}; - -} // namespace MHDProperties -} // namespace VertexCFD - -#endif // VERTEXCFD_FULLINDUCTIONMHDPROPERTIES_HPP diff --git a/src/full_induction_mhd_solver/mhd_properties/unit_test/CMakeLists.txt b/src/full_induction_mhd_solver/mhd_properties/unit_test/CMakeLists.txt deleted file mode 100644 index 282142a..0000000 --- a/src/full_induction_mhd_solver/mhd_properties/unit_test/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - FullInductionMHDProperties - ) diff --git a/src/full_induction_mhd_solver/mhd_properties/unit_test/tstFullInductionMHDProperties.cpp b/src/full_induction_mhd_solver/mhd_properties/unit_test/tstFullInductionMHDProperties.cpp deleted file mode 100644 index 047393d..0000000 --- a/src/full_induction_mhd_solver/mhd_properties/unit_test/tstFullInductionMHDProperties.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "full_induction_mhd_solver/mhd_properties/VertexCFD_FullInductionMHDProperties.hpp" - -#include - -#include - -#include - -using namespace VertexCFD::MHDProperties; - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace Test -{ -class FullInductionMHDPropertiesTest : public ::testing::Test -{ - protected: - std::unique_ptr mhd_props; - - virtual void SetUp() override; - virtual void SetUpConstantProps(const bool set_alpha); - virtual void SetUpVariableResistivity(); -}; - -// Set up default (minimal inputs) -void FullInductionMHDPropertiesTest::SetUp() -{ - Teuchos::ParameterList mhd_params; - mhd_props = std::make_unique(mhd_params); -} - -// Set all properties with constant resistivity -void FullInductionMHDPropertiesTest::SetUpConstantProps(const bool set_alpha) -{ - Teuchos::ParameterList mhd_params; - mhd_params.set("Build Magnetic Correction Potential Equation", true); - mhd_params.set("Build Resistive Flux", true); - mhd_params.set("Vacuum Magnetic Permeability", 0.5); - mhd_params.set("Variable Resistivity", false); - mhd_params.set("Resistivity", 1.5); - mhd_params.set("Hyperbolic Divergence Cleaning Speed", 1.8); - if (set_alpha) - { - mhd_params.set("Magnetic Correction Damping Factor", 2.0); - } - mhd_props = std::make_unique(mhd_params); -} - -// Set variable resistivity -void FullInductionMHDPropertiesTest::SetUpVariableResistivity() -{ - Teuchos::ParameterList mhd_params; - mhd_params.set("Build Magnetic Correction Potential Equation", false); - mhd_params.set("Build Resistive Flux", true); - mhd_params.set("Variable Resistivity", true); - mhd_props = std::make_unique(mhd_params); -} -//---------------------------------------------------------------------------// - -// Test default properties -TEST_F(FullInductionMHDPropertiesTest, defaults) -{ - FullInductionMHDPropertiesTest::SetUp(); - - EXPECT_FALSE(mhd_props->variableResistivity()); - - EXPECT_FALSE(mhd_props->buildMagnCorr()); - - EXPECT_FALSE(mhd_props->buildResistiveFlux()); - - const double mu_0 = mhd_props->vacuumMagneticPermeability(); - EXPECT_DOUBLE_EQ(1.0, mu_0); - - const double eta = mhd_props->resistivity(); - EXPECT_TRUE(std::isnan(eta)); - - const double c_h = mhd_props->hyperbolicDivergenceCleaningSpeed(); - EXPECT_DOUBLE_EQ(0.0, c_h); - - const double alpha = mhd_props->magneticCorrectionDampingFactor(); - EXPECT_DOUBLE_EQ(0.0, alpha); -} - -// Test constant properties with derived alpha -TEST_F(FullInductionMHDPropertiesTest, derived_alpha) -{ - FullInductionMHDPropertiesTest::SetUpConstantProps(false); - - EXPECT_FALSE(mhd_props->variableResistivity()); - - EXPECT_TRUE(mhd_props->buildMagnCorr()); - - EXPECT_TRUE(mhd_props->buildResistiveFlux()); - - const double mu_0 = mhd_props->vacuumMagneticPermeability(); - EXPECT_DOUBLE_EQ(0.5, mu_0); - - const double eta = mhd_props->resistivity(); - EXPECT_EQ(1.5, eta); - - const double c_h = mhd_props->hyperbolicDivergenceCleaningSpeed(); - EXPECT_DOUBLE_EQ(1.8, c_h); - - const double alpha = mhd_props->magneticCorrectionDampingFactor(); - EXPECT_DOUBLE_EQ(10.0, alpha); -} - -// Test constant properties with input alpha -TEST_F(FullInductionMHDPropertiesTest, set_alpha) -{ - FullInductionMHDPropertiesTest::SetUpConstantProps(true); - - EXPECT_FALSE(mhd_props->variableResistivity()); - - EXPECT_TRUE(mhd_props->buildMagnCorr()); - - EXPECT_TRUE(mhd_props->buildResistiveFlux()); - - const double mu_0 = mhd_props->vacuumMagneticPermeability(); - EXPECT_DOUBLE_EQ(0.5, mu_0); - - const double eta = mhd_props->resistivity(); - EXPECT_EQ(1.5, eta); - - const double c_h = mhd_props->hyperbolicDivergenceCleaningSpeed(); - EXPECT_DOUBLE_EQ(1.8, c_h); - - const double alpha = mhd_props->magneticCorrectionDampingFactor(); - EXPECT_DOUBLE_EQ(2.0, alpha); -} - -// Test variable resistivity -TEST_F(FullInductionMHDPropertiesTest, variable_resistivity) -{ - const std::string exp_msg - = "No closure models currently exist to evaluate variable " - "resistivity. Use a constant resistivity only."; - EXPECT_THROW( - try { - FullInductionMHDPropertiesTest::SetUpVariableResistivity(); - } catch (const std::runtime_error& e) { - EXPECT_EQ(exp_msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test diff --git a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.cpp b/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.cpp deleted file mode 100644 index 417916d..0000000 --- a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp" -#include "VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleLSVOFConvectiveFlux) diff --git a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp b/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp deleted file mode 100644 index 8755369..0000000 --- a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension convective flux evaluation for LSVOF Navier-Stokes equations -//---------------------------------------------------------------------------// -template -class IncompressibleLSVOFConvectiveFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleLSVOFConvectiveFlux(const panzer::IntegrationRule& ir, - const std::string& flux_prefix = "", - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _continuity_flux; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _momentum_flux; - - private: - PHX::MDField _rho; - PHX::MDField _pressure; - Kokkos::Array, - num_space_dim> - _velocity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_HPP diff --git a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux_impl.hpp b/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux_impl.hpp deleted file mode 100644 index 51a8d56..0000000 --- a/src/incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux_impl.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_IMPL_HPP - -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleLSVOFConvectiveFlux:: - IncompressibleLSVOFConvectiveFlux(const panzer::IntegrationRule& ir, - const std::string& flux_prefix, - const std::string& field_prefix) - : _continuity_flux(flux_prefix + "CONVECTIVE_FLUX_continuity", ir.dl_vector) - , _rho(field_prefix + "density", ir.dl_scalar) - , _pressure(field_prefix + "lagrange_pressure", ir.dl_scalar) -{ - // Evaluated fields - this->addEvaluatedField(_continuity_flux); - - Utils::addEvaluatedVectorField(*this, ir.dl_vector, _momentum_flux, - flux_prefix + "CONVECTIVE_FLUX_" - "momentum_"); - - // Dependent fields - this->addDependentField(_rho); - this->addDependentField(_pressure); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _velocity, field_prefix + "velocity_"); - - this->setName("Incompressible LSVOF Convective Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLSVOFConvectiveFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLSVOFConvectiveFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _continuity_flux.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - _continuity_flux(cell, point, dim) - = _rho(cell, point) * _velocity[dim](cell, point); - - for (int mom_dim = 0; mom_dim < num_space_dim; ++mom_dim) - { - _momentum_flux[mom_dim](cell, point, dim) - = _rho(cell, point) * _velocity[dim](cell, point) - * _velocity[mom_dim](cell, point); - if (mom_dim == dim) - { - _momentum_flux[mom_dim](cell, point, dim) - += _pressure(cell, point); - } - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELSVOFCONVECTIVEFLUX_IMPL_HPP diff --git a/src/incompressible_lsvof_solver/closure_models/unit_test/CMakeLists.txt b/src/incompressible_lsvof_solver/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index f9dfc5e..0000000 --- a/src/incompressible_lsvof_solver/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - IncompressibleLSVOFConvectiveFlux - ) - diff --git a/src/incompressible_lsvof_solver/closure_models/unit_test/tstIncompressibleLSVOFConvectiveFlux.cpp b/src/incompressible_lsvof_solver/closure_models/unit_test/tstIncompressibleLSVOFConvectiveFlux.cpp deleted file mode 100644 index e435d9b..0000000 --- a/src/incompressible_lsvof_solver/closure_models/unit_test/tstIncompressibleLSVOFConvectiveFlux.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include "incompressible_lsvof_solver/closure_models/VertexCFD_Closure_IncompressibleLSVOFConvectiveFlux.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u; - double _v; - double _w; - - PHX::MDField pressure; - PHX::MDField vel_0; - PHX::MDField vel_1; - PHX::MDField vel_2; - PHX::MDField rho; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w) - : _u(u) - , _v(v) - , _w(w) - , pressure("lagrange_pressure", ir.dl_scalar) - , vel_0("velocity_0", ir.dl_scalar) - , vel_1("velocity_1", ir.dl_scalar) - , vel_2("velocity_2", ir.dl_scalar) - , rho("density", ir.dl_scalar) - - { - this->addEvaluatedField(pressure); - this->addEvaluatedField(vel_0); - this->addEvaluatedField(vel_1); - this->addEvaluatedField(vel_2); - this->addEvaluatedField(rho); - - this->setName( - "Incompressible LSVOF Convective Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - pressure.deep_copy(0.75); - vel_0.deep_copy(_u); - vel_1.deep_copy(_v); - vel_2.deep_copy(_w); - rho.deep_copy(1.375); - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize velocity components and dependents - const double _nanval = std::numeric_limits::quiet_NaN(); - const double u = 0.25; - const double v = 0.5; - const double w = num_space_dim > 2 ? 0.125 : _nanval; - const double p = 0.75; - const double rho = 1.375; - - auto deps = Teuchos::rcp(new Dependencies(ir, u, v, w)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - const auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleLSVOFConvectiveFlux(ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_continuity_flux); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_momentum_flux[dim]); - - test_fixture.evaluate(); - - const auto fc_cont - = test_fixture.getTestFieldData(eval->_continuity_flux); - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_momentum_flux[0]); - const auto fc_mom_1 - = test_fixture.getTestFieldData(eval->_momentum_flux[1]); - - const int num_point = ir.num_points; - - // Expected values - const double exp_cont_flux[3] = {rho * u, rho * v, rho * w}; - const double exp_mom_0_flux[3] - = {rho * u * u + p, - rho * u * v, - num_space_dim == 3 ? rho * u * w : _nanval}; - const double exp_mom_1_flux[3] - = {rho * v * u, - rho * v * v + p, - num_space_dim == 3 ? rho * v * w : _nanval}; - const double exp_mom_2_flux[3] - = {rho * w * u, - rho * w * v, - num_space_dim == 3 ? rho * w * w + p : _nanval}; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_EQ(exp_cont_flux[dim], fieldValue(fc_cont, 0, qp, dim)); - EXPECT_EQ(exp_mom_0_flux[dim], fieldValue(fc_mom_0, 0, qp, dim)); - EXPECT_EQ(exp_mom_1_flux[dim], fieldValue(fc_mom_1, 0, qp, dim)); - - if (num_space_dim > 2) // 3D mesh - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_momentum_flux[2]); - EXPECT_EQ(exp_mom_2_flux[dim], - fieldValue(fc_mom_2, 0, qp, dim)); - } - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleLSVOFConvectiveFlux2D, Residual) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleLSVOFConvectiveFlux2D, Jacobian) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleLSVOFConvectiveFlux3D, Residual) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleLSVOFConvectiveFlux3D, Jacobian) -{ - testEval(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.cpp deleted file mode 100644 index 42d6396..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleCavityLid.hpp" -#include "VertexCFD_BoundaryState_IncompressibleCavityLid_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleCavityLid) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp deleted file mode 100644 index c87aea2..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Lid condition for 2D or 3D lid-driven cavity problem -//---------------------------------------------------------------------------// -template -class IncompressibleCavityLid : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleCavityLid( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - PHX::MDField - _boundary_grad_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - PHX::MDField - _grad_temperature; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - PHX::MDField _ip_coords; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; - int _wall_dir; - int _vel_dir; - double _h; - double _u_wall; - double _T_bc; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid_impl.hpp deleted file mode 100644 index b0ce5c6..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid_impl.hpp +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleCavityLid::IncompressibleCavityLid( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _ir_degree(ir.cubature_degree) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) - , _wall_dir(bc_params.get("Wall Normal Direction")) - , _vel_dir(bc_params.get("Velocity Direction")) - , _h(bc_params.get("Half Width")) - , _u_wall(bc_params.get("Wall Velocity")) - , _T_bc(std::numeric_limits::quiet_NaN()) -{ - // Check that dimensions make sense - if (_wall_dir >= num_space_dim) - { - const std::string msg - = "Wall normal direction greater than " - "number of solution dimensions in Cavity Lid boundary " - "condition."; - - throw std::runtime_error(msg); - } - if (_vel_dir >= num_space_dim) - { - const std::string msg - = "Velocity direction greater than " - "number of solution dimensions in Cavity Lid boundary " - "condition."; - - throw std::runtime_error(msg); - } - if (_wall_dir == _vel_dir) - { - const std::string msg - = "Velocity direction is same as wall normal in " - "Cavity Lid boundary condition."; - - throw std::runtime_error(msg); - } - - // Add evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - if (_solve_temp) - { - _T_bc = bc_params.get("Temperature"); - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - // Add dependent fields - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - if (_solve_temp) - this->addDependentField(_grad_temperature); - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Cavity Lid " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleCavityLid::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleCavityLid::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleCavityLid::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set lagrange pressure - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - // Set boundary values for velocity components - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - if (vel_dim == _vel_dir) - { - _boundary_velocity[vel_dim](cell, point) = _u_wall; - - for (int dim = 0; dim < num_space_dim; ++dim) - { - if (dim != _wall_dir) - { - _boundary_velocity[vel_dim](cell, point) *= pow( - 1.0 - - pow(_ip_coords(cell, point, dim) / _h, - 18.0), - 2.0); - } - } - } - else - { - _boundary_velocity[vel_dim](cell, point) = 0.0; - } - } - - if (_solve_temp) - _boundary_temperature(cell, point) = _T_bc; - - // Set gradients at boundaries. - for (int d = 0; d < num_grad_dim; ++d) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLECAVITYLID_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.cpp deleted file mode 100644 index 3d226b3..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleDirichlet) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp deleted file mode 100644 index af968f3..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleDirichlet : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleDirichlet( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - PHX::MDField _boundary_temperature; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - - PHX::MDField - _boundary_grad_temperature; - - private: - double _time; - double _time_init; - double _time_final; - Kokkos::Array _a_vel; - Kokkos::Array _b_vel; - double _T_dirichlet; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - PHX::MDField - _grad_temperature; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet_impl.hpp deleted file mode 100644 index 50fec48..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet_impl.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This function should be used for Dirichlet boundary conditions. A ramping -// in time can be enabled for all variables or only a few. Be aware that the -// ramping in time does not include any logic with the characteristics and thus -// should only be used for non-transient runs. -//---------------------------------------------------------------------------// -template -IncompressibleDirichlet::IncompressibleDirichlet( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) -{ - // Calculate the coefficients 'a' and 'b' for the linear time ramping - // f(t) = a * t + b for each variable - _time_init = bc_params.isType("Time Initial") - ? bc_params.get( - "Time " - "Initial") - : 0.0; - _time_final = bc_params.isType("Time Final") - ? bc_params.get("Time Final") - : 1.0E-06; - const double dt = _time_final - _time_init; - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const std::string vel_string = "velocity_" + std::to_string(vel_dim); - const auto vel_final = bc_params.get(vel_string); - const auto vel_init = bc_params.isType(vel_string + "_init") - ? bc_params.get(vel_string + "_init") - : vel_final; - _a_vel[vel_dim] = (vel_final - vel_init) / dt; - _b_vel[vel_dim] = vel_init - _a_vel[vel_dim] * _time_init; - } - - // Add evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - if (_solve_temp) - { - _T_dirichlet = bc_params.get("temperature"); - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - // Add dependent fields - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - if (_solve_temp) - this->addDependentField(_grad_temperature); - - this->setName("Boundary State Incompressible Dirichlet " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleDirichlet::evaluateFields( - typename Traits::EvalData workset) -{ - // Get time and make sure it only varies between '_time_init' and - // '_time_final' - _time = std::max(workset.time, _time_init); - _time = std::min(_time, _time_final); - - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleDirichlet::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Assign time-dependent boundary values - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_velocity[vel_dim](cell, point) - = _a_vel[vel_dim] * _time + _b_vel[vel_dim]; - } - if (_solve_temp) - _boundary_temperature(cell, point) = _T_dirichlet; - - // Set gradients - for (int d = 0; d < num_grad_dim; ++d) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEDIRICHLET_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.cpp deleted file mode 100644 index 10a6e93..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp" -#include "VertexCFD_BoundaryState_IncompressibleFreeSlip_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleFreeSlip) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp deleted file mode 100644 index 232c493..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleFreeSlip : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleFreeSlip( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - PHX::MDField _temperature; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField - _grad_temperature; - - PHX::MDField - _normals; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip_impl.hpp deleted file mode 100644 index 54d626a..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip_impl.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleFreeSlip::IncompressibleFreeSlip( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _temperature("temperature", ir.dl_scalar) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) -{ - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - if (_solve_temp) - { - this->addDependentField(_temperature); - this->addEvaluatedField(_boundary_temperature); - this->addDependentField(_grad_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - - this->addDependentField(_normals); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Free Slip " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleFreeSlip::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleFreeSlip::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _normals.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set boundary Lagrange pressure - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - // Compute \vec{vel} \cdot \vec{n} - scalar_type vel_dot_n = 0.0; - scalar_type grad_T_dot_n = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - vel_dot_n += _velocity[dim](cell, point) - * _normals(cell, point, dim); - if (_solve_temp) - { - grad_T_dot_n += _grad_temperature(cell, point, dim) - * _normals(cell, point, dim); - } - } - - if (_solve_temp) - _boundary_temperature(cell, point) = _temperature(cell, point); - - // Set boundary velocity and boundary gradients - for (int dim = 0; dim < num_grad_dim; ++dim) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, dim) - = _grad_lagrange_pressure(cell, point, dim); - } - - _boundary_velocity[dim](cell, point) - = _velocity[dim](cell, point) - - vel_dot_n * _normals(cell, point, dim); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, dim) - = _grad_velocity[vel_dim](cell, point, dim); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, dim) - = _grad_temperature(cell, point, dim) - - grad_T_dot_n * _normals(cell, point, dim); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEFREESLIP_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.cpp deleted file mode 100644 index 80232de..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp" -#include "VertexCFD_BoundaryState_IncompressibleLaminarFlow_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleLaminarFlow) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp deleted file mode 100644 index 85e333d..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Isothermal no-slip rotating wall. -//---------------------------------------------------------------------------// -template -class IncompressibleLaminarFlow - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleLaminarFlow( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - PHX::MDField - _boundary_grad_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - PHX::MDField - _grad_temperature; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - PHX::MDField _ip_coords; - bool _solve_temp; - std::string _continuity_model_name; - enum class ContinuityModel - { - AC, - EDAC - }; - ContinuityModel _continuity_model; - - double _min; - double _max; - double _vel_avg; - double _vel_max; - double _T_bc; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow_impl.hpp deleted file mode 100644 index 676fd89..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow_impl.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleLaminarFlow::IncompressibleLaminarFlow( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _ir_degree(ir.cubature_degree) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _min(bc_params.get("Minimum height")) - , _max(bc_params.get("Maximum height")) - , _vel_avg(bc_params.get("Average velocity")) - , _vel_max(num_space_dim == 2 ? 3.0 / 2.0 * _vel_avg : 2.0 * _vel_avg) - , _T_bc(std::numeric_limits::quiet_NaN()) -{ - if (continuity_model_name == "AC") - { - _continuity_model = ContinuityModel::AC; - } - else if (continuity_model_name == "EDAC") - { - _continuity_model = ContinuityModel::EDAC; - } - - // Add evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - if (_solve_temp) - { - _T_bc = bc_params.get("Temperature"); - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - // Add dependent fields - this->addDependentField(_lagrange_pressure); - if (_continuity_model == ContinuityModel::EDAC) - this->addDependentField(_grad_lagrange_pressure); - if (_solve_temp) - this->addDependentField(_grad_temperature); - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Laminar Flow " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set lagrange pressure - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - // Set boundary values for velocity components - double r2 = _ip_coords(cell, point, 1) * _ip_coords(cell, point, 1); - const double H = 0.5 * (_max - _min); - _boundary_velocity[1](cell, point) = 0.0; - if (num_grad_dim == 3) - { - r2 += _ip_coords(cell, point, 2) * _ip_coords(cell, point, 2); - _boundary_velocity[2](cell, point) = 0.0; - } - _boundary_velocity[0](cell, point) = _vel_max - * (1.0 - r2 / (H * H)); - - if (_solve_temp) - _boundary_temperature(cell, point) = _T_bc; - - // Set gradients at boundaries. - for (int d = 0; d < num_grad_dim; ++d) - { - if (_continuity_model == ContinuityModel::EDAC) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.cpp deleted file mode 100644 index ba59f16..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleNoSlip) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp deleted file mode 100644 index 3d17077..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleNoSlip : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleNoSlip( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - PHX::MDField - _grad_temperature; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; - double _T_wall; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip_impl.hpp deleted file mode 100644 index 6ccc13a..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip_impl.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleNoSlip::IncompressibleNoSlip( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) - , _T_wall(std::numeric_limits::quiet_NaN()) -{ - // Evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - if (_solve_temp) - { - _T_wall = bc_params.get("Wall Temperature"); - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - - // Dependent fields - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - if (_solve_temp) - this->addEvaluatedField(_grad_temperature); - - this->setName("Boundary State Incompressible No-Slip"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleNoSlip::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleNoSlip::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_lagrange_pressure.extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set boundary values - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - _boundary_velocity[vel_dim](cell, point) = 0.0; - if (_solve_temp) - _boundary_temperature(cell, point) = _T_wall; - - // Set gradients at the boundaries. - for (int d = 0; d < num_grad_dim; ++d) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLENOSLIP_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.cpp deleted file mode 100644 index 0b12d7a..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp" -#include "VertexCFD_BoundaryState_IncompressiblePressureOutflow_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressiblePressureOutflow) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp deleted file mode 100644 index 2acde23..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Isothermal and isotropic pressure outflow boundary condition. -//---------------------------------------------------------------------------// -template -class IncompressiblePressureOutflow - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressiblePressureOutflow( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - - PHX::MDField _boundary_temperature; - - PHX::MDField - _boundary_grad_temperature; - - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - - private: - PHX::MDField - _grad_lagrange_pressure; - - PHX::MDField _temperature; - - PHX::MDField - _grad_temperature; - - Kokkos::Array, - num_space_dim> - _velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; - double _p_back; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow_impl.hpp deleted file mode 100644 index 6bffe4f..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow_impl.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressiblePressureOutflow:: - IncompressiblePressureOutflow( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _temperature("temperature", ir.dl_scalar) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) - , _p_back(bc_params.get("Back Pressure")) -{ - // Add evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - if (_solve_temp) - { - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - // Add dependent fields - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - if (_solve_temp) - { - this->addDependentField(_temperature); - this->addDependentField(_grad_temperature); - } - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Pressure Outflow " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressiblePressureOutflow::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressiblePressureOutflow::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_velocity[0].extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Assign velocity boundaries - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_velocity[vel_dim](cell, point) - = _velocity[vel_dim](cell, point); - } - - // Assign boundary conditions for primitive variables - _boundary_lagrange_pressure(cell, point) = _p_back; - - // Temperature equation - if (_solve_temp) - _boundary_temperature(cell, point) = _temperature(cell, point); - - // Set boundary gradients - for (int d = 0; d < num_grad_dim; ++d) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEPRESSUREOUTFLOW_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.cpp deleted file mode 100644 index 8279d7d..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp" -#include "VertexCFD_BoundaryState_IncompressibleRotatingWall_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleRotatingWall) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp deleted file mode 100644 index d207d4c..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Isothermal no-slip rotating wall. -//---------------------------------------------------------------------------// -template -class IncompressibleRotatingWall - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleRotatingWall( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - int _ir_degree; - bool _set_lagrange_pressure; - double _lp_wall; - int _ir_index; - double _a_vel, _b_vel; - double _time_init, _time_final; - double _angular_velocity; - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; - double _T_wall; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - PHX::MDField - _grad_temperature; - - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall_impl.hpp deleted file mode 100644 index 238d901..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall_impl.hpp +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleRotatingWall::IncompressibleRotatingWall( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& bc_params, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _ir_degree(ir.cubature_degree) - , _set_lagrange_pressure(bc_params.isType("Lagrange Pressure")) - , _lp_wall(std::numeric_limits::quiet_NaN()) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) - , _T_wall(std::numeric_limits::quiet_NaN()) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) -{ - // Compute the coefficients for the linear ramping in time f(x) = a * time - // + b - _time_init = bc_params.isType("Time Initial") - ? bc_params.get("Time Initial") - : 0.0; - _time_final = bc_params.isType("Time Final") - ? bc_params.get("Time Final") - : 1.0E-06; - - if (_set_lagrange_pressure) - _lp_wall = bc_params.get("Lagrange Pressure"); - - const auto angular_velocity_final - = bc_params.get("Angular Velocity"); - const auto angular_velocity_init = bc_params.isType( - "Angular Velocity " - "Initial") - ? bc_params.get( - "Angular Velocity " - "Initial") - : angular_velocity_final; - const double dt = _time_final - _time_init; - _a_vel = (angular_velocity_final - angular_velocity_init) / dt; - _b_vel = angular_velocity_init - _a_vel * _time_init; - - // Add evaluated fields - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - if (_solve_temp) - { - _T_wall = bc_params.get("Wall Temperature"); - this->addEvaluatedField(_boundary_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - - // Add dependent fields - this->addEvaluatedField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - if (_solve_temp) - this->addEvaluatedField(_grad_temperature); - - this->setName("Boundary State Incompressible Rotating Wall " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingWall::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingWall::evaluateFields( - typename Traits::EvalData workset) -{ - // Update time and make sure that 'time' only varies between '_time_init' - // and '_time_final' - const double time = workset.time < _time_init ? _time_init - : workset.time >= _time_final ? _time_final - : workset.time; - _angular_velocity = _a_vel * time + _b_vel; - - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingWall::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _boundary_grad_velocity[0].extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set lagrange pressure - if (_set_lagrange_pressure) - _boundary_lagrange_pressure(cell, point) = _lp_wall; - - else - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - // Set wall temperature - if (_solve_temp) - _boundary_temperature(cell, point) = _T_wall; - - // Set boundary values for velocity components - Kokkos::Array vel_bnd{}; - vel_bnd[0] = -_angular_velocity * _ip_coords(cell, point, 1); - vel_bnd[1] = _angular_velocity * _ip_coords(cell, point, 0); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - _boundary_velocity[vel_dim](cell, point) = vel_bnd[vel_dim]; - - // Set gradients at boundaries. - for (int d = 0; d < num_grad_dim; ++d) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, d) - = _grad_lagrange_pressure(cell, point, d); - } - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, d) - = _grad_velocity[vel_dim](cell, point, d); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, d) - = _grad_temperature(cell, point, d); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEROTATINGWALL_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.cpp deleted file mode 100644 index da6dbf7..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleSymmetry.hpp" -#include "VertexCFD_BoundaryState_IncompressibleSymmetry_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleSymmetry) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp deleted file mode 100644 index 8ed42b9..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleSymmetry : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleSymmetry( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - PHX::MDField _temperature; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField - _grad_temperature; - - PHX::MDField - _normals; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry_impl.hpp deleted file mode 100644 index a4f9f6c..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry_impl.hpp +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleSymmetry::IncompressibleSymmetry( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _temperature("temperature", ir.dl_scalar) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) -{ - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - if (_solve_temp) - { - this->addDependentField(_temperature); - this->addEvaluatedField(_boundary_temperature); - this->addDependentField(_grad_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - - this->addDependentField(_normals); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Free Slip " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSymmetry::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSymmetry::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _normals.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set boundary Lagrange pressure - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - // Compute components of flow variables normal to surface - scalar_type vel_dot_n = 0.0; - scalar_type grad_T_dot_n = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - vel_dot_n += _velocity[dim](cell, point) - * _normals(cell, point, dim); - - if (_solve_temp) - { - grad_T_dot_n += _grad_temperature(cell, point, dim) - * _normals(cell, point, dim); - } - } - - Kokkos::Array grad_vel_dot_n{}; - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - grad_vel_dot_n[vel_dim] = 0.0; - - for (int dim = 0; dim < num_grad_dim; ++dim) - { - grad_vel_dot_n[vel_dim] - += _grad_velocity[vel_dim](cell, point, dim) - * _normals(cell, point, dim); - } - } - - if (_solve_temp) - _boundary_temperature(cell, point) = _temperature(cell, point); - - // Set boundary velocity and boundary gradients - for (int dim = 0; dim < num_grad_dim; ++dim) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, dim) - = _grad_lagrange_pressure(cell, point, dim); - } - - _boundary_velocity[dim](cell, point) - = _velocity[dim](cell, point) - - vel_dot_n * _normals(cell, point, dim); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - _boundary_grad_velocity[vel_dim](cell, point, dim) - = _grad_velocity[vel_dim](cell, point, dim) - - grad_vel_dot_n[vel_dim] - * _normals(cell, point, dim); - } - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, dim) - = _grad_temperature(cell, point, dim) - - grad_T_dot_n * _normals(cell, point, dim); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLESYMMETRY_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.cpp deleted file mode 100644 index a27ecd8..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_IncompressibleWallFunction.hpp" -#include "VertexCFD_BoundaryState_IncompressibleWallFunction_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleWallFunction) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp deleted file mode 100644 index 540bc79..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleWallFunction - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleWallFunction( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_lagrange_pressure; - PHX::MDField - _boundary_grad_lagrange_pressure; - PHX::MDField _boundary_temperature; - Kokkos::Array, - num_space_dim> - _boundary_velocity; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _boundary_grad_velocity; - PHX::MDField - _boundary_grad_temperature; - - private: - PHX::MDField _boundary_u_tau; - PHX::MDField _boundary_y_plus; - PHX::MDField _boundary_nu_t; - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_lagrange_pressure; - PHX::MDField _temperature; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField - _grad_temperature; - - PHX::MDField - _normals; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _rho; - double _nu; - bool _solve_temp; - std::string _continuity_model_name; - bool _is_edac; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction_impl.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction_impl.hpp deleted file mode 100644 index 44bd37f..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction_impl.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleWallFunction::IncompressibleWallFunction( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& continuity_model_name) - : _boundary_lagrange_pressure("BOUNDARY_lagrange_pressure", ir.dl_scalar) - , _boundary_grad_lagrange_pressure("BOUNDARY_GRAD_lagrange_pressure", - ir.dl_vector) - , _boundary_temperature("BOUNDARY_temperature", ir.dl_scalar) - , _boundary_grad_temperature("BOUNDARY_GRAD_temperature", ir.dl_vector) - , _boundary_u_tau("BOUNDARY_friction_velocity", ir.dl_scalar) - , _boundary_y_plus("BOUNDARY_y_plus", ir.dl_scalar) - , _boundary_nu_t("BOUNDARY_turbulent_eddy_viscosity", ir.dl_scalar) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - , _temperature("temperature", ir.dl_scalar) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _rho(fluid_prop.constantDensity()) - , _nu(fluid_prop.constantKinematicViscosity()) - , _solve_temp(fluid_prop.solveTemperature()) - , _continuity_model_name(continuity_model_name) - , _is_edac(continuity_model_name == "EDAC" ? true : false) -{ - this->addEvaluatedField(_boundary_lagrange_pressure); - if (_is_edac) - this->addEvaluatedField(_boundary_grad_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _boundary_velocity, "BOUNDARY_velocity_"); - - Utils::addEvaluatedVectorField(*this, - ir.dl_vector, - _boundary_grad_velocity, - "BOUNDARY_GRAD_velocity_"); - - if (_solve_temp) - { - this->addDependentField(_temperature); - this->addEvaluatedField(_boundary_temperature); - this->addDependentField(_grad_temperature); - this->addEvaluatedField(_boundary_grad_temperature); - } - - // Turbulence quantites obtained from corresponding wall function boundary - // state used for turbulence quantities. - this->addDependentField(_boundary_u_tau); - this->addDependentField(_boundary_y_plus); - this->addDependentField(_boundary_nu_t); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - this->addDependentField(_lagrange_pressure); - if (_is_edac) - this->addDependentField(_grad_lagrange_pressure); - - this->addDependentField(_normals); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Boundary State Incompressible Wall Function " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleWallFunction::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleWallFunction::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - const int num_grad_dim = _normals.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set boundary Lagrange pressure - _boundary_lagrange_pressure(cell, point) - = _lagrange_pressure(cell, point); - - if (_solve_temp) - { - _boundary_temperature(cell, point) = _temperature(cell, point); - } - - // Set boundary velocity and boundary gradients - for (int dim = 0; dim < num_grad_dim; ++dim) - { - if (_is_edac) - { - _boundary_grad_lagrange_pressure(cell, point, dim) - = _grad_lagrange_pressure(cell, point, dim); - } - // Initialize boundary velocity and temperature gradient to - // interior fields (modified in subsequent loop) - _boundary_velocity[dim](cell, point) - = _velocity[dim](cell, point); - - if (_solve_temp) - { - _boundary_grad_temperature(cell, point, dim) - = _grad_temperature(cell, point, dim); - } - - // NOTE:this only works for 2D cases with an inward-facing - // wall normal in the POSITIVE Y direction - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - // Set du/dy according to wall function formulation - if ((dim == 1) && (vel_dim == 0)) - { - _boundary_grad_velocity[vel_dim](cell, point, dim) - = _boundary_u_tau(cell, point) - / _boundary_y_plus(cell, point) - * _velocity[vel_dim](cell, point) - / (_rho * (_nu + _boundary_nu_t(cell, point))); - } - // Set other components to interior values - else - { - _boundary_grad_velocity[vel_dim](cell, point, dim) - = _grad_velocity[vel_dim](cell, point, dim); - } - - // Subtract normal component from velocity and temperature - // gradient fields - _boundary_velocity[dim](cell, point) - -= _velocity[vel_dim](cell, point) - * _normals(cell, point, vel_dim) - * _normals(cell, point, dim); - - if (_solve_temp) - { - // TODO: this BC currently assumes an insulated wall. - // A future MR should implement the correct wall - // function for the energy equation. - _boundary_grad_temperature(cell, point, dim) - -= _grad_temperature(cell, point, vel_dim) - * _normals(cell, point, vel_dim) - * _normals(cell, point, dim); - } - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_INCOMPRESSIBLEWALLFUNCTION_IMPL_HPP diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.cpp b/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.cpp deleted file mode 100644 index a187379..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::IncompressibleBoundaryStateFactory) diff --git a/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp b/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp deleted file mode 100644 index 4fbf65e..0000000 --- a/src/incompressible_solver/boundary_conditions/VertexCFD_IncompressibleBoundaryState_Factory.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef VERTEXCFD_INCOMPRESSIBLEBOUNDARYSTATE_FACTORY_HPP -#define VERTEXCFD_INCOMPRESSIBLEBOUNDARYSTATE_FACTORY_HPP - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp" -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp" - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleBoundaryStateFactory -{ - public: - static Teuchos::RCP> - create(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const Teuchos::ParameterList& user_params) - { - // Space dimension - constexpr int num_space_dim = NumSpaceDim; - - // Equation of state - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - std::string continuity_model_name - = user_params.isType("Continuity Model") - ? user_params.get("Continuity Model") - : "AC"; - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_buoyancy - = user_params.isType("Build Buoyancy Source") - ? user_params.get("Build Buoyancy Source") - : false; - const bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Buoyancy Source", build_buoyancy); - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Loop over boundary conditions - Teuchos::RCP> state; - bool found_model = false; - - if (bc_params.isType("Type")) - { - if (bc_params.get("Type") == "No-Slip") - { - state = Teuchos::rcp( - new IncompressibleNoSlip( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Dirichlet") - { - state = Teuchos::rcp( - new IncompressibleDirichlet( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Pressure Outflow") - { - state = Teuchos::rcp( - new IncompressiblePressureOutflow( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Free Slip") - { - state = Teuchos::rcp( - new IncompressibleFreeSlip( - ir, fluid_prop, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Symmetry") - { - state = Teuchos::rcp( - new IncompressibleSymmetry( - ir, fluid_prop, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Rotating Wall") - { - state = Teuchos::rcp( - new IncompressibleRotatingWall( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Laminar Flow") - { - state = Teuchos::rcp( - new IncompressibleLaminarFlow( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Cavity Lid") - { - state = Teuchos::rcp( - new IncompressibleCavityLid( - ir, fluid_prop, bc_params, continuity_model_name)); - found_model = true; - } - - if (bc_params.get("Type") == "Velocity Wall Function") - { - state = Teuchos::rcp( - new IncompressibleWallFunction( - ir, fluid_prop, continuity_model_name)); - found_model = true; - } - } - - if (!found_model) - { - std::string msg = "\n\nBoundary state " - + bc_params.get("Type") - + " failed to build.\n"; - msg += "The boundary conditions implemented in VertexCFD are:\n"; - msg += "No-Slip,\n"; - msg += "Dirichlet,\n"; - msg += "Free Slip,\n"; - msg += "Pressure Outflow,\n"; - msg += "Rotating Wall,\n"; - msg += "Laminar Flow,\n"; - msg += "Symmetry,\n"; - msg += "Cavity Lid\n"; - msg += "Velocity Wall Function\n"; - msg += "\n"; - throw std::runtime_error(msg); - } - - return state; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INCOMPRESSIBLEBOUNDARYSTATE_FACTORY_HPP diff --git a/src/incompressible_solver/boundary_conditions/unit_test/CMakeLists.txt b/src/incompressible_solver/boundary_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index 1d1e334..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - TimeTransientIncompressibleDirichlet - IncompressibleFreeSlip - IncompressibleNoSlip - IncompressibleSymmetry - TimeTransientIncompressibleRotatingWall - IncompressiblePressureOutflow - IncompressibleBoundaryLaminarFlow - IncompressibleCavityLid - IncompressibleWallFunction - ) diff --git a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_cavity_flow_reference.py b/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_cavity_flow_reference.py deleted file mode 100644 index b6d1c5e..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_cavity_flow_reference.py +++ /dev/null @@ -1,34 +0,0 @@ -# Functions -def bc2d(y, h, U_0): - u = U_0 * pow(1.0 - pow(y / h, 18.0), 2.0) - return u, 0.0 - - -def bc3d(y, z, h, U_0): - u = U_0 * pow(1.0 - pow(y / h, 18.0), 2.0) * pow(1.0 - pow(z / h, 18.0), - 2.0) - return u, 0.0, 0.0 - - -# IC parameters -h = 1.0 -U_0 = 2.0 - -# Compute ic values in 2D -y = 0.7375 -u, v = bc2d(y, h, U_0) - -# Print ic values -print("\n2D laminar flow:") -print("u: ", u) -print("v: ", v) - -# Compute ic values in 3D -z = 0.9775 -u, v, w = bc3d(y, z, h, U_0) - -# Print ic values -print("\n3D laminar flow:") -print("u: ", u) -print("v: ", v) -print("w: ", w) diff --git a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_laminar_flow_reference.py b/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_laminar_flow_reference.py deleted file mode 100644 index 5461e4e..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_laminar_flow_reference.py +++ /dev/null @@ -1,41 +0,0 @@ -# Functions -def ic2d(y, h_min, h_max, vel_avg): - coeff = 3.0 / 2.0 - H = 0.5 * (h_max - h_min) - u = vel_avg * coeff * (1.0 - y * y / (H * H)) - return u, 0.0 - - -def ic3d(y, z, h_min, h_max, vel_avg): - coeff = 2.0 - H = 0.5 * (h_max - h_min) - r2 = y * y + z * z - u = vel_avg * coeff * (1.0 - r2 / (H * H)) - return u, 0.0, 0.0 - - -# IC parameters -h_min = -2.0 -h_max = 2.2 -vel_avg = 3.0 - -# Compute ic values in 2D -y = 0.7375 -u, v = ic2d(y, h_max, h_min, vel_avg) - -# Print ic values -print("\n2D laminar flow:") -print("phi: ", 0.0) -print("u: ", u) -print("v: ", v) - -# Compute ic values in 3D -z = 0.9775 -u, v, w = ic3d(y, z, h_min, h_max, vel_avg) - -# Print ic values -print("\n3D laminar flow:") -print("phi: ", 0.0) -print("u: ", u) -print("v: ", v) -print("w: ", w) diff --git a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_symmetry_reference.py b/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_symmetry_reference.py deleted file mode 100644 index 4ce0ab4..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_symmetry_reference.py +++ /dev/null @@ -1,27 +0,0 @@ -import numpy as np - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, -0.5], [0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, -0.5, -0.125], [0.5, 1.0, 0.25], - [-0.75, -1.5, -0.375]]) - -normals = [-0.02, 0.04, -0.06] - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing boundary velocity gradient in ", dim, "D\n") - - grad_vel = grad_vel_2D - if (dim == 3): - grad_vel = grad_vel_3D - - normals_dim = normals[:dim] - - for d in range(dim): - boundary_grad_u = grad_vel[d] - np.array( - np.dot(grad_vel[d], normals_dim)) * normals_dim - print("Boundary gradient of component ", d, " is: ", boundary_grad_u, - "\n") - - print("\n") diff --git a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_wall_function_reference.py b/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_wall_function_reference.py deleted file mode 100644 index d2550cd..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/doc/incompressible_wall_function_reference.py +++ /dev/null @@ -1,51 +0,0 @@ -import numpy as np - -# Wall function quantities -u_tau = 2.0 -y_plus = 13.0 -nu_t = 5.0 - -# Flow quantities -rho = 1.0 -nu = 2.5 -vel = np.array([1.0, -2.0, 3.0]) - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, -0.5], [0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, -0.5, -0.125], [0.5, 1.0, 0.25], - [-0.75, -1.5, -0.375]]) - -grad_temp = np.array([1.0, -2.0, 3.0]) - -normals = [-0.02, 0.04, -0.06] - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing boundary velocity gradient in ", dim, "D\n") - - grad_vel = grad_vel_2D - if (dim == 3): - grad_vel = grad_vel_3D - - vel_dim = vel[:dim] - grad_temp_dim = grad_temp[:dim] - normals_dim = normals[:dim] - - boundary_vel = vel_dim - np.array(np.dot(vel_dim, - normals_dim)) * normals_dim - print("Boundary velocity: ", boundary_vel) - - boundary_grad_temp = grad_temp_dim - np.array( - np.dot(grad_temp_dim, normals_dim)) * normals_dim - - print("Boundary temperature gradient: ", boundary_grad_temp) - - for d in range(dim): - boundary_grad_u = grad_vel[d] - if d == 0: - boundary_grad_u[1] = u_tau / y_plus * vel_dim[d] / rho / (nu + - nu_t) - print("Boundary gradient of component ", d, " is: ", boundary_grad_u, - "\n") - print("\n") diff --git a/src/incompressible_solver/boundary_conditions/unit_test/doc/time_dependent_incompressible_rotating_wall_reference.py b/src/incompressible_solver/boundary_conditions/unit_test/doc/time_dependent_incompressible_rotating_wall_reference.py deleted file mode 100644 index 669c6ea..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/doc/time_dependent_incompressible_rotating_wall_reference.py +++ /dev/null @@ -1,99 +0,0 @@ -from decimal import * - -getcontext().prec = 60 -places = Decimal(10)**-16 - - -# Print value -def show(name, d): - print(name + " = ", str(d.quantize(places)).rstrip('0')) - - -# Function to calculate boundary values -def calculate_boundary_values(omega, - omega_init, - x, - y, - dim, - time, - time_init=Decimal(0.0), - time_final=Decimal(10)**-6): - # Compute time transient coefficients - if (time < time_init): time = time_init - if (time > time_final): time = time_final - - dt = time_final - time_init - a = (omega - omega_init) / dt - b = omega_init - a * time_init - - # Compute velocity components - u = -(a * time + b) * y - v = (a * time + b) * x - w = Decimal(0.0) # the z-component of the velocity is always 0.0 - - # Show values - show("u", u) - show("v", v) - if (dim == 3): show("w", w) - - -# Constant input arguments for all tests -omega = Decimal(2.0) -x = Decimal('0.7375') -y = Decimal('0.9775') - -# 2-D cases -# Steady case (no transient) -dim = 2 -print("\n2-D test case 'steady") -omega_init = omega - -time = Decimal(3.0) - -calculate_boundary_values(omega, omega_init, x, y, dim, time) - -# time > time_final (same result as steady-state case) -print("\n2-D test case 'time > time_final'") -omega_init = Decimal(1.0) - -time_init = Decimal(0.5) -time_final = Decimal(3.0) -time = Decimal(3.5) - -calculate_boundary_values(omega, omega_init, x, y, dim, time, time_init, - time_final) - -# time < time_init -print("\n2-D test case 'time < time_init'") -omega_init = Decimal(1.0) - -time_init = Decimal(0.5) -time_final = Decimal(3.0) -time = Decimal('0.2') - -calculate_boundary_values(omega, omega_init, x, y, dim, time, time_init, - time_final) - -# time_init < time < time_final -print("\n2-D test case 'time_init < time < time_final'") -omega_init = Decimal(1.0) - -time_init = Decimal(0.5) -time_final = Decimal(3.0) -time = Decimal(1.5) - -calculate_boundary_values(omega, omega_init, x, y, dim, time, time_init, - time_final) - -# 3-D case -dim = 3 -# time_init < time < time_final -print("\n3-D test case 'time_init < time < time_final'") -omega_init = Decimal(1.0) - -time_init = Decimal(0.5) -time_final = Decimal(3.0) -time = Decimal(1.5) - -calculate_boundary_values(omega, omega_init, x, y, dim, time, time_init, - time_final) diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleBoundaryLaminarFlow.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleBoundaryLaminarFlow.cpp deleted file mode 100644 index 1b9d510..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleBoundaryLaminarFlow.cpp +++ /dev/null @@ -1,363 +0,0 @@ -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleLaminarFlow.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u0, _u1, _u2; - bool _build_tmp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double u0, - const double u1, - const double u2, - const bool build_tmp_equ, - const ContinuityModel continuity_model) - : _u0(u0) - , _u1(u1) - , _u2(u2) - , _build_tmp_equ(build_tmp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - - { - this->addEvaluatedField(_lagrange_pressure); - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_tmp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - this->setName("Incompressible Laminar Flow Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _lagrange_pressure.deep_copy(_u0 + _u1); - _grad_velocity_0.deep_copy(_u0 * _u0); - _grad_velocity_1.deep_copy(_u1 * _u1); - _grad_velocity_2.deep_copy(_u2 * _u2); - if (_build_tmp_equ) - _grad_temperature.deep_copy(_u0 - _u1); - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure.deep_copy(_u0 - _u1); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Set non-trivial values for quadrature points - test_fixture.int_values->ip_coordinates(0, 0, 1) = 0.7375; - if (num_space_dim == 3) - test_fixture.int_values->ip_coordinates(0, 0, 2) = 0.9775; - - // Create dependencies - double nanval = std::numeric_limits::quiet_NaN(); - const double u0 = 0.2; - const double u1 = 0.3; - const double u2 = num_space_dim == 3 ? 0.4 : nanval; - const double vel[3] = {u0, u1, u2}; - - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, u0, u1, u2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("Minimum height", -2.0); - bc_params.set("Maximum height", 2.2); - bc_params.set("Average velocity", 3.0); - const double T_bc = build_temp_equ ? 4.0 : nanval; - if (build_temp_equ) - bc_params.set("Temperature", T_bc); - - // Create evaluator. - auto laminar_eval = Teuchos::rcp( - new BoundaryCondition::IncompressibleLaminarFlow( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(laminar_eval); - - // Add required test fields. - test_fixture.registerTestField( - laminar_eval->_boundary_lagrange_pressure); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - test_fixture.registerTestField( - laminar_eval->_boundary_velocity[vel_dim]); - test_fixture.registerTestField( - laminar_eval->_boundary_grad_velocity[vel_dim]); - } - - if (build_temp_equ) - { - test_fixture.registerTestField( - laminar_eval->_boundary_temperature); - test_fixture.registerTestField( - laminar_eval->_boundary_grad_temperature); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - laminar_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate boundary values. - test_fixture.evaluate(); - - const double u_ref = num_space_dim == 2 ? 3.944993622448979 - : 3.9599829931972788; - - // Check boundary values. - const auto boundary_phi_result = test_fixture.getTestFieldData( - laminar_eval->_boundary_lagrange_pressure); - - const int num_point = boundary_phi_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(u0 + u1, fieldValue(boundary_phi_result, 0, qp)); - - const auto boundary_velocity_0_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_velocity[0]); - EXPECT_DOUBLE_EQ(u_ref, fieldValue(boundary_velocity_0_result, 0, qp)); - for (int vel_dim = 1; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_velocity[vel_dim]); - EXPECT_DOUBLE_EQ(0.0, - fieldValue(boundary_velocity_d_result, 0, qp)); - } - - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(T_bc, - fieldValue(boundary_temperature_result, 0, qp)); - } - - for (int d = 0; d < num_space_dim; ++d) - { - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const double exp_val = vel[vel_dim] * vel[vel_dim]; - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (build_temp_equ) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - laminar_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal LaminarFlow -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal LaminarFlow -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible LaminarFlow -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible LaminarFlow -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleCavityLid.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleCavityLid.cpp deleted file mode 100644 index 48a3ab2..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleCavityLid.cpp +++ /dev/null @@ -1,525 +0,0 @@ -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleCavityLid.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u0, _u1, _u2; - bool _build_tmp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double u0, - const double u1, - const double u2, - const bool build_tmp_equ, - const ContinuityModel continuity_model) - : _u0(u0) - , _u1(u1) - , _u2(u2) - , _build_tmp_equ(build_tmp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_tmp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - this->setName("Incompressible Cavity Lid Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _lagrange_pressure.deep_copy(_u0 + _u1); - _grad_velocity_0.deep_copy(_u0 * _u0); - _grad_velocity_1.deep_copy(_u1 * _u1); - _grad_velocity_2.deep_copy(_u2 * _u2); - if (_build_tmp_equ) - _grad_temperature.deep_copy(_u0 - _u1); - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure.deep_copy(_u0 - _u1); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Set non-trivial values for quadrature points - test_fixture.int_values->ip_coordinates(0, 0, 1) = 0.7375; - if (num_space_dim == 3) - test_fixture.int_values->ip_coordinates(0, 0, 2) = 0.9775; - - // Create dependencies - const double nanval = std::numeric_limits::quiet_NaN(); - const double u0 = 0.2; - const double u1 = 0.3; - const double u2 = num_space_dim == 3 ? 0.4 : nanval; - const double vel[3] = {u0, u1, u2}; - - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, u0, u1, u2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("Wall Normal Direction", 0); - bc_params.set("Velocity Direction", 1); - bc_params.set("Wall Velocity", 2.0); - bc_params.set("Half Width", 1.0); - const double T_bc = build_temp_equ ? 4.0 : nanval; - if (build_temp_equ) - bc_params.set("Temperature", T_bc); - - // Create evaluator. - auto bc_eval = Teuchos::rcp( - new BoundaryCondition::IncompressibleCavityLid( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(bc_eval); - - // Add required test fields. - test_fixture.registerTestField( - bc_eval->_boundary_lagrange_pressure); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - test_fixture.registerTestField( - bc_eval->_boundary_velocity[vel_dim]); - test_fixture.registerTestField( - bc_eval->_boundary_grad_velocity[vel_dim]); - } - if (build_temp_equ) - { - test_fixture.registerTestField( - bc_eval->_boundary_temperature); - test_fixture.registerTestField( - bc_eval->_boundary_grad_temperature); - } - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - bc_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate boundary values. - test_fixture.evaluate(); - - const double u_exp = num_space_dim == 3 ? 0.22404972666527662 - : 1.9833708189379662; - - // Check boundary values. - const auto boundary_phi_result = test_fixture.getTestFieldData( - bc_eval->_boundary_lagrange_pressure); - - const int num_point = boundary_phi_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(u0 + u1, fieldValue(boundary_phi_result, 0, qp)); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - bc_eval->_boundary_velocity[vel_dim]); - if (vel_dim == 1) - { - EXPECT_DOUBLE_EQ( - u_exp, fieldValue(boundary_velocity_d_result, 0, qp)); - } - else - { - EXPECT_DOUBLE_EQ( - 0.0, fieldValue(boundary_velocity_d_result, 0, qp)); - } - } - - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - bc_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(T_bc, - fieldValue(boundary_temperature_result, 0, qp)); - } - - for (int d = 0; d < num_space_dim; ++d) - { - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const double exp_val = vel[vel_dim] * vel[vel_dim]; - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - bc_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (build_temp_equ) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - bc_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - bc_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal cavity lid -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal cavity lid -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible cavity lid -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible cavity lid -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -//---------------------------------------------------------------------------// -// Error message cases -enum class ErrorMessages -{ - wall, - velocity, - equal -}; - -//---------------------------------------------------------------------------// -// Test for error messages -template -void testErrors(const ErrorMessages error_message) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create dependencies - const double nanval = std::numeric_limits::quiet_NaN(); - const double u2 = num_space_dim == 3 ? 0.4 : nanval; - - auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, 1.0, 2.0, u2, false, ContinuityModel::EDAC)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - std::string msg = ""; - int wall_dir = 0; - int vel_dir = 1; - - // Set wall and velocity direction based on error message - switch (error_message) - { - case (ErrorMessages::wall): - wall_dir = num_space_dim; - msg - = "Wall normal direction greater than " - "number of solution dimensions in Cavity Lid boundary " - "condition."; - break; - case (ErrorMessages::velocity): - vel_dir = num_space_dim; - msg - = "Velocity direction greater than " - "number of solution dimensions in Cavity Lid boundary " - "condition."; - break; - case (ErrorMessages::equal): - vel_dir = 0; - msg - = "Velocity direction is same as wall normal in " - "Cavity Lid boundary condition."; - break; - } - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("Wall Normal Direction", wall_dir); - bc_params.set("Velocity Direction", vel_dir); - bc_params.set("Wall Velocity", 2.0); - bc_params.set("Half Width", 1.0); - - using eval = BoundaryCondition:: - IncompressibleCavityLid; - - ASSERT_THROW( - try { - auto cavity_lid_eval - = eval(*test_fixture.ir, fluid_prop, bc_params, "EDAC"); - } catch (const std::runtime_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct ErrorTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto error_message = info.param; - switch (error_message) - { - case (ErrorMessages::wall): - return "wall"; - case (ErrorMessages::velocity): - return "velocity"; - case (ErrorMessages::equal): - return "equal"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// Residual evaluation, 2D -TEST_P(ErrorTest, residual2D) -{ - ErrorMessages error_message; - error_message = GetParam(); - testErrors(error_message); -} - -//---------------------------------------------------------------------------// -// Residual evaluation, 3D -TEST_P(ErrorTest, residual3D) -{ - ErrorMessages error_message; - error_message = GetParam(); - testErrors(error_message); -} - -//---------------------------------------------------------------------------// -// Jacobian evaluation, 2D -TEST_P(ErrorTest, jacobian2D) -{ - ErrorMessages error_message; - error_message = GetParam(); - testErrors(error_message); -} - -//---------------------------------------------------------------------------// -// Jacobian evaluation, 3D -TEST_P(ErrorTest, jacobian3D) -{ - ErrorMessages error_message; - error_message = GetParam(); - testErrors(error_message); -} - -//---------------------------------------------------------------------------// -// Generate test suite with errors -INSTANTIATE_TEST_SUITE_P(CavityLidBC, - ErrorTest, - testing::Values(ErrorMessages::wall, - ErrorMessages::velocity, - ErrorMessages::equal), - ErrorTest::ParamNameGenerator{}); - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleFreeSlip.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleFreeSlip.cpp deleted file mode 100644 index f650b33..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleFreeSlip.cpp +++ /dev/null @@ -1,409 +0,0 @@ -#include - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleFreeSlip.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _phi, _u_0, _u_1, _u_2; - bool _build_temp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField _temperature; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - - PHX::MDField _normals; - - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double phi, - const double u_0, - const double u_1, - const double u_2, - const bool build_temp_equ, - const ContinuityModel continuity_model) - : _phi(phi) - , _u_0(u_0) - , _u_1(u_1) - , _u_2(u_2) - , _build_temp_equ(build_temp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - if (_build_temp_equ) - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - - this->addEvaluatedField(_normals); - - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_temp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - - this->setName("Incompressible FreeSlip Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - // Set scalar variables - _lagrange_pressure.deep_copy(_phi); - _velocity_0.deep_copy(_u_0); - _velocity_1.deep_copy(_u_1); - _velocity_2.deep_copy(_u_2); - if (_build_temp_equ) - _temperature.deep_copy(_u_0 + _u_1); - - Kokkos::parallel_for( - "incompressible free slip test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION - void operator()(const int c) const - { - const int num_point = _lagrange_pressure.extent(1); - const int num_space_dim = _grad_velocity_0.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - // Set gradient and normal vectors - for (int d = 0; d < num_space_dim; ++d) - { - _normals(c, qp, d) = 0.01 * (d + 1) * (d * num_point + 1) - * (qp + 1); - int dqp = (qp + 1) * (d + num_point + 1); - _grad_velocity_0(c, qp, d) = _u_0 * dqp; - _grad_velocity_1(c, qp, d) = _u_1 * dqp; - _grad_velocity_2(c, qp, d) = _u_2 * dqp; - if (_build_temp_equ) - _grad_temperature(c, qp, d) = (_u_0 + _u_1) * dqp; - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure(c, qp, d) = (_u_0 - _u_1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Initialize values and create dependencies - const double phi = 1.5; - const double u_0 = 2.0; - const double u_1 = 2.5; - const double u_2 - = num_space_dim == 3 ? 2.75 : std::numeric_limits::quiet_NaN(); - double vel[3] = {u_0, u_1, u_2}; - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, phi, u_0, u_1, u_2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create free slip evaluator. - auto free_slip_eval = Teuchos::rcp( - new BoundaryCondition:: - IncompressibleFreeSlip( - *test_fixture.ir, fluid_prop, continuity_model_name)); - test_fixture.registerEvaluator(free_slip_eval); - - // Add required test fields. - test_fixture.registerTestField( - free_slip_eval->_boundary_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - free_slip_eval->_boundary_velocity[dim]); - test_fixture.registerTestField( - free_slip_eval->_boundary_grad_velocity[dim]); - } - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - free_slip_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate incompressible free slip - test_fixture.evaluate(); - - // Get free slip field - auto boundary_lagrange_pressure_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_lagrange_pressure); - - // Loop over quadrature points and mesh dimension - const int num_point = boundary_lagrange_pressure_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - // Lagrange pressure - EXPECT_DOUBLE_EQ(phi, - fieldValue(boundary_lagrange_pressure_result, 0, qp)); - - // Calculate velocity boundary using normal vector - const auto normals - = test_fixture.getTestFieldData(dep_eval->_normals); - const double n0 = fieldValue(normals, 0, qp, 0); - const double n1 = fieldValue(normals, 0, qp, 1); - const double n2 = num_space_dim == 3 - ? fieldValue(normals, 0, qp, 2) - : std::numeric_limits::quiet_NaN(); - - double u_dot_n = u_0 * n0 + u_1 * n1; - if (num_space_dim == 3) - u_dot_n += u_2 * n2; - const double u_bnd = u_0 - u_dot_n * n0; - const double v_bnd = u_1 - u_dot_n * n1; - const double w_bnd = num_space_dim == 3 ? u_2 - u_dot_n * n2 : 0.0; - double vel_bnd[3] = {u_bnd, v_bnd, w_bnd}; - - double grad_T_dot_n = 0.0; - if (build_temp_equ) - { - for (int d = 0; d < num_space_dim; ++d) - { - const int dqp = (qp + 1) * (d + num_point + 1); - grad_T_dot_n += (u_0 + u_1) * dqp - * fieldValue(normals, 0, qp, d); - } - } - - // Temperature - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(u_0 + u_1, - fieldValue(boundary_temperature_result, 0, qp)); - } - - // Loop over mesh dimension to assert gradient vectors - for (int d = 0; d < num_space_dim; ++d) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_velocity[d]); - EXPECT_DOUBLE_EQ(vel_bnd[d], - fieldValue(boundary_velocity_d_result, 0, qp)); - - const int dqp = (qp + 1) * (d + num_point + 1); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - vel[vel_dim] * dqp, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - if (build_temp_equ) - { - const auto grad_temp_result - = test_fixture.getTestFieldData( - dep_eval->_grad_temperature); - const double grad_temp_ref - = fieldValue(grad_temp_result, 0, qp, d) - - grad_T_dot_n * fieldValue(normals, 0, qp, d); - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - grad_temp_ref, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - free_slip_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal freeSlip -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal freeSlip -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible freeSlip -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible freeSlip -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleNoSlip.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleNoSlip.cpp deleted file mode 100644 index 3e27eed..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleNoSlip.cpp +++ /dev/null @@ -1,370 +0,0 @@ -#include - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleNoSlip.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _phi, _u_0, _u_1, _u_2; - bool _build_tmp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - double phi, - double u_0, - double u_1, - double u_2, - const bool build_tmp_equ, - const ContinuityModel continuity_model) - : _phi(phi) - , _u_0(u_0) - , _u_1(u_1) - , _u_2(u_2) - , _build_tmp_equ(build_tmp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (build_tmp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - - this->setName("Incompressible NoSlip Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - // Set scalar variables - _lagrange_pressure.deep_copy(_phi); - _velocity_0.deep_copy(_u_0); - _velocity_1.deep_copy(_u_1); - _velocity_2.deep_copy(_u_2); - - Kokkos::parallel_for( - "incompressible no slip test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION - void operator()(const int c) const - { - const int num_point = _lagrange_pressure.extent(1); - const int num_space_dim = _grad_velocity_0.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - // Set gradient and normal vectors - for (int d = 0; d < num_space_dim; ++d) - { - const int dqp = (qp + 1) * (d + num_point + 1); - _grad_velocity_0(c, qp, d) = _u_0 * dqp; - _grad_velocity_1(c, qp, d) = _u_1 * dqp; - _grad_velocity_2(c, qp, d) = _u_2 * dqp; - if (_build_tmp_equ) - _grad_temperature(c, qp, d) = (_u_0 + _u_1) * dqp; - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure(c, qp, d) = (_u_0 - _u_1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Initialize values and create dependencies - const double phi = 1.5; - const double u = 2.0; - const double v = 2.5; - const double w - = num_space_dim == 3 ? 2.75 : std::numeric_limits::quiet_NaN(); - const double vel[3] = {u, v, w}; - const double T_wall = u * v; - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, phi, u, v, w, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Boundary condition - Teuchos::ParameterList bc_params; - if (build_temp_equ) - bc_params.set("Wall Temperature", T_wall); - - // Create no slip evaluator. - auto no_slip_eval = Teuchos::rcp( - new BoundaryCondition::IncompressibleNoSlip( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(no_slip_eval); - - // Add required test fields. - test_fixture.registerTestField( - no_slip_eval->_boundary_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - no_slip_eval->_boundary_velocity[dim]); - test_fixture.registerTestField( - no_slip_eval->_boundary_grad_velocity[dim]); - } - - if (build_temp_equ) - { - test_fixture.registerTestField( - no_slip_eval->_boundary_grad_temperature); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - no_slip_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate incompressible free slip - test_fixture.evaluate(); - - // Get no slip field - auto boundary_lagrange_pressure_result - = test_fixture.getTestFieldData( - no_slip_eval->_boundary_lagrange_pressure); - - // Loop over quadrature points and mesh dimension - const int num_point = boundary_lagrange_pressure_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - // Lagrange pressure - EXPECT_DOUBLE_EQ(phi, - fieldValue(boundary_lagrange_pressure_result, 0, qp)); - - // Loop over mesh dimension to assert velocity and gradient vectors - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - no_slip_eval->_boundary_velocity[vel_dim]); - EXPECT_DOUBLE_EQ(0.0, - fieldValue(boundary_velocity_d_result, 0, qp)); - } - - for (int d = 0; d < num_space_dim; ++d) - { - const int dqp = (qp + 1) * (d + num_point + 1); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - no_slip_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - vel[vel_dim] * dqp, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - - if (build_temp_equ) - { - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - no_slip_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - (vel[0] + vel[1]) * dqp, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - no_slip_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -// 2-D incompressible isothermal no slip -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal no slip -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible no slip -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible no slip -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressiblePressureOutflow.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressiblePressureOutflow.cpp deleted file mode 100644 index 3bf3dc5..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressiblePressureOutflow.cpp +++ /dev/null @@ -1,359 +0,0 @@ -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressiblePressureOutflow.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u0, _u1, _u2; - bool _build_tmp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - PHX::MDField _temperature; - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double u0, - const double u1, - const double u2, - const bool build_tmp_equ, - const ContinuityModel continuity_model) - : _u0(u0) - , _u1(u1) - , _u2(u2) - , _build_tmp_equ(build_tmp_equ) - , _continuity_model(continuity_model) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - if (_build_tmp_equ) - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_tmp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - this->setName( - "Incompressible pressure outflow Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _velocity_0.deep_copy(_u0); - _velocity_1.deep_copy(_u1); - _velocity_2.deep_copy(_u2); - if (_build_tmp_equ) - _temperature.deep_copy(_u0 + _u1); - _grad_velocity_0.deep_copy(_u0 * _u0); - _grad_velocity_1.deep_copy(_u1 * _u1); - _grad_velocity_2.deep_copy(_u2 * _u2); - if (_build_tmp_equ) - _grad_temperature.deep_copy(_u0 - _u1); - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure.deep_copy(_u0 - _u1); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int num_grad_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Create dependencies - double nanval = std::numeric_limits::quiet_NaN(); - const double phi = 0.1; - const double u0 = 0.2; - const double u1 = 0.3; - const double u2 = num_space_dim == 3 ? 0.4 : nanval; - const double vel[3] = {u0, u1, u2}; - - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, u0, u1, u2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("Back Pressure", phi); - - // Create evaluator. - auto press_eval = Teuchos::rcp( - new BoundaryCondition::IncompressiblePressureOutflow( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(press_eval); - - // Add required test fields. - test_fixture.registerTestField( - press_eval->_boundary_lagrange_pressure); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - test_fixture.registerTestField( - press_eval->_boundary_velocity[vel_dim]); - test_fixture.registerTestField( - press_eval->_boundary_grad_velocity[vel_dim]); - } - if (build_temp_equ) - { - test_fixture.registerTestField( - press_eval->_boundary_temperature); - test_fixture.registerTestField( - press_eval->_boundary_grad_temperature); - } - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - press_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate boundary values. - test_fixture.evaluate(); - - // Check boundary values. - const auto boundary_phi_result = test_fixture.getTestFieldData( - press_eval->_boundary_lagrange_pressure); - - const int num_point = boundary_phi_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(phi, fieldValue(boundary_phi_result, 0, qp)); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - press_eval->_boundary_velocity[vel_dim]); - EXPECT_DOUBLE_EQ(vel[vel_dim], - fieldValue(boundary_velocity_d_result, 0, qp)); - } - - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - press_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(vel[0] + vel[1], - fieldValue(boundary_temperature_result, 0, qp)); - } - - for (int d = 0; d < num_grad_dim; ++d) - { - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const double exp_val = vel[vel_dim] * vel[vel_dim]; - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - press_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (build_temp_equ) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - press_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = vel[0] - vel[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - press_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal pressure outflow -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal pressure outflow -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible pressure outflow -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible pressure outflow -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleSymmetry.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleSymmetry.cpp deleted file mode 100644 index 2ec9c56..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleSymmetry.cpp +++ /dev/null @@ -1,438 +0,0 @@ -#include - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleSymmetry.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _phi, _u_0, _u_1, _u_2; - bool _build_temp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField _temperature; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - - PHX::MDField _normals; - - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double phi, - const double u_0, - const double u_1, - const double u_2, - const bool build_temp_equ, - const ContinuityModel continuity_model) - : _phi(phi) - , _u_0(u_0) - , _u_1(u_1) - , _u_2(u_2) - , _build_temp_equ(build_temp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - if (_build_temp_equ) - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - - this->addEvaluatedField(_normals); - - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_temp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - - this->setName("Incompressible Symmetry Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - // Set scalar variables - _lagrange_pressure.deep_copy(_phi); - _velocity_0.deep_copy(_u_0); - _velocity_1.deep_copy(_u_1); - _velocity_2.deep_copy(_u_2); - if (_build_temp_equ) - _temperature.deep_copy(_u_0 + _u_1); - - Kokkos::parallel_for( - "incompressible free slip test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION - void operator()(const int c) const - { - const int num_point = _lagrange_pressure.extent(1); - const int num_space_dim = _grad_velocity_0.extent(2); - - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - // Set gradient and normal vectors - for (int d = 0; d < num_space_dim; ++d) - { - const int dimqp = (d + 1) * pow(-1, d + 1); - - _normals(c, qp, d) = 0.02 * dimqp; - - _grad_velocity_0(c, qp, d) = 0.250 * dimqp; - _grad_velocity_1(c, qp, d) = 0.500 * dimqp; - _grad_velocity_2(c, qp, d) = 0.125 * dimqp; - - if (_build_temp_equ) - _grad_temperature(c, qp, d) = (_u_0 + _u_1) * dimqp; - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure(c, qp, d) = (_u_0 - _u_1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Initialize values and create dependencies - const double _nanval = std::numeric_limits::quiet_NaN(); - const double phi = 1.5; - const double u_0 = 2.0; - const double u_1 = 2.5; - const double u_2 = num_space_dim == 3 ? 2.75 : _nanval; - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, phi, u_0, u_1, u_2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create symmetry evaluator. - auto symm_eval = Teuchos::rcp( - new BoundaryCondition:: - IncompressibleSymmetry( - *test_fixture.ir, fluid_prop, continuity_model_name)); - test_fixture.registerEvaluator(symm_eval); - - // Add required test fields. - test_fixture.registerTestField( - symm_eval->_boundary_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - symm_eval->_boundary_velocity[dim]); - test_fixture.registerTestField( - symm_eval->_boundary_grad_velocity[dim]); - } - - if (build_temp_equ) - { - test_fixture.registerTestField( - symm_eval->_boundary_temperature); - test_fixture.registerTestField( - symm_eval->_boundary_grad_temperature); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - symm_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate incompressible symmetry - test_fixture.evaluate(); - - // Get symmetry field - auto boundary_lagrange_pressure_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_lagrange_pressure); - - // Create expected velocity gradient - const double grad_u_2D[3] = {-0.2495, -0.499, _nanval}; - const double grad_u_3D[3] = {-0.2486, -0.4972, -0.1243}; - const double* grad_u = (num_space_dim == 3) ? grad_u_3D : grad_u_2D; - - const double grad_v_2D[3] = {0.499, 0.998, _nanval}; - const double grad_v_3D[3] = {0.4972, 0.9944, 0.2486}; - const double* grad_v = (num_space_dim == 3) ? grad_v_3D : grad_v_2D; - - const double grad_w_2D[3] = {_nanval, _nanval, _nanval}; - const double grad_w_3D[3] = {-0.7458, -1.4916, -0.3729}; - const double* grad_w = (num_space_dim == 3) ? grad_w_3D : grad_w_2D; - - const double* grad_vel[3] = {grad_u, grad_v, grad_w}; - - // Loop over quadrature points and mesh dimension - const int num_point = boundary_lagrange_pressure_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - // Lagrange pressure - EXPECT_DOUBLE_EQ(phi, - fieldValue(boundary_lagrange_pressure_result, 0, qp)); - - // Calculate velocity boundary using normal vector - const auto normals - = test_fixture.getTestFieldData(dep_eval->_normals); - const double n0 = fieldValue(normals, 0, qp, 0); - const double n1 = fieldValue(normals, 0, qp, 1); - const double n2 = num_space_dim == 3 - ? fieldValue(normals, 0, qp, 2) - : std::numeric_limits::quiet_NaN(); - - double u_dot_n = u_0 * n0 + u_1 * n1; - if (num_space_dim == 3) - u_dot_n += u_2 * n2; - const double u_bnd = u_0 - u_dot_n * n0; - const double v_bnd = u_1 - u_dot_n * n1; - const double w_bnd = num_space_dim == 3 ? u_2 - u_dot_n * n2 : 0.0; - double vel_bnd[3] = {u_bnd, v_bnd, w_bnd}; - - double grad_T_dot_n = 0.0; - if (build_temp_equ) - { - for (int d = 0; d < num_space_dim; ++d) - { - const int dqp = (d + 1) * std::pow(-1, d + 1); - grad_T_dot_n += (u_0 + u_1) * dqp - * fieldValue(normals, 0, qp, d); - } - } - - // Temperature - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(u_0 + u_1, - fieldValue(boundary_temperature_result, 0, qp)); - } - - // Loop over mesh dimension to assert gradient vectors - for (int d = 0; d < num_space_dim; ++d) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_velocity[d]); - EXPECT_DOUBLE_EQ(vel_bnd[d], - fieldValue(boundary_velocity_d_result, 0, qp)); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_grad_velocity[vel_dim]); - - EXPECT_DOUBLE_EQ( - grad_vel[d][vel_dim], - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (build_temp_equ) - { - const auto grad_temp_result - = test_fixture.getTestFieldData( - dep_eval->_grad_temperature); - const double grad_temp_ref - = fieldValue(grad_temp_result, 0, qp, d) - - grad_T_dot_n * fieldValue(normals, 0, qp, d); - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - grad_temp_ref, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = u_0 - u_1; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal symmetry -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal symmetry -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible symmetry -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible symmetry -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleWallFunction.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleWallFunction.cpp deleted file mode 100644 index 8049c8b..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstIncompressibleWallFunction.cpp +++ /dev/null @@ -1,424 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleWallFunction.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _phi, _u_0, _u_1, _u_2; - bool _build_temp_equ; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField _temperature; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - - PHX::MDField _boundary_u_tau; - PHX::MDField _boundary_y_plus; - PHX::MDField _boundary_nu_t; - - PHX::MDField _normals; - - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double phi, - const double u_0, - const double u_1, - const double u_2, - const bool build_temp_equ, - const ContinuityModel continuity_model) - : _phi(phi) - , _u_0(u_0) - , _u_1(u_1) - , _u_2(u_2) - , _build_temp_equ(build_temp_equ) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _boundary_u_tau("BOUNDARY_friction_velocity", ir.dl_scalar) - , _boundary_y_plus("BOUNDARY_y_plus", ir.dl_scalar) - , _boundary_nu_t("BOUNDARY_turbulent_eddy_viscosity", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - if (_build_temp_equ) - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - this->addEvaluatedField(_boundary_u_tau); - this->addEvaluatedField(_boundary_y_plus); - this->addEvaluatedField(_boundary_nu_t); - - this->addEvaluatedField(_normals); - - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_build_temp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - - this->setName("Incompressible Wall Function Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - // Set scalar variables - _lagrange_pressure.deep_copy(_phi); - _velocity_0.deep_copy(_u_0); - _velocity_1.deep_copy(_u_1); - _velocity_2.deep_copy(_u_2); - _boundary_u_tau.deep_copy(2.0); - _boundary_y_plus.deep_copy(13.0); - _boundary_nu_t.deep_copy(5.0); - if (_build_temp_equ) - _temperature.deep_copy(_u_0 + _u_1); - - Kokkos::parallel_for( - "incompressible wall function test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION - void operator()(const int c) const - { - const int num_point = _lagrange_pressure.extent(1); - const int num_space_dim = _grad_velocity_0.extent(2); - - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - // Set gradient and normal vectors - for (int d = 0; d < num_space_dim; ++d) - { - const int dimqp = (d + 1) * pow(-1, d + 1); - - _normals(c, qp, d) = 0.02 * dimqp; - - _grad_velocity_0(c, qp, d) = 0.250 * dimqp; - _grad_velocity_1(c, qp, d) = 0.500 * dimqp; - _grad_velocity_2(c, qp, d) = 0.125 * dimqp; - - if (_build_temp_equ) - _grad_temperature(c, qp, d) = (_u_0 + _u_1) * dimqp; - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure(c, qp, d) = (_u_0 - _u_1); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ, const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Initialize values and create dependencies - const double _nanval = std::numeric_limits::quiet_NaN(); - const double phi = 1.5; - const double u_0 = 1.0; - const double u_1 = -2.0; - const double u_2 = num_space_dim == 3 ? 3.0 : _nanval; - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, phi, u_0, u_1, u_2, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 2.5); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create wall function evaluator. - const auto wf_eval = Teuchos::rcp( - new BoundaryCondition:: - IncompressibleWallFunction( - *test_fixture.ir, fluid_prop, continuity_model_name)); - test_fixture.registerEvaluator(wf_eval); - - // Add required test fields. - test_fixture.registerTestField( - wf_eval->_boundary_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - wf_eval->_boundary_velocity[dim]); - test_fixture.registerTestField( - wf_eval->_boundary_grad_velocity[dim]); - } - - if (build_temp_equ) - { - test_fixture.registerTestField( - wf_eval->_boundary_temperature); - test_fixture.registerTestField( - wf_eval->_boundary_grad_temperature); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - wf_eval->_boundary_grad_lagrange_pressure); - } - - // Evaluate incompressible wall function - test_fixture.evaluate(); - - // Get wall function field - auto boundary_lagrange_pressure_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_lagrange_pressure); - - // Create expected velocity gradient - const double grad_u_2D[3] = {-0.25, 0.020512820512820513, _nanval}; - const double grad_u_3D[3] = {-0.25, 0.020512820512820513, -0.75}; - const double* grad_u = (num_space_dim == 3) ? grad_u_3D : grad_u_2D; - - const double grad_v_2D[3] = {-0.5, 1.0, _nanval}; - const double grad_v_3D[3] = {-0.5, 1.0, -1.5}; - const double* grad_v = (num_space_dim == 3) ? grad_v_3D : grad_v_2D; - - const double grad_w_2D[3] = {_nanval, _nanval, _nanval}; - const double grad_w_3D[3] = {-0.125, 0.25, -0.375}; - const double* grad_w = (num_space_dim == 3) ? grad_w_3D : grad_w_2D; - - const double* grad_vel[3] = {grad_u, grad_v, grad_w}; - - const double grad_temp_2D[3] = {0.998, -1.996, _nanval}; - const double grad_temp_3D[3] = {0.9944, -1.9888, 2.9832}; - const double* grad_temp = (num_space_dim == 3) ? grad_temp_3D - : grad_temp_2D; - - const double* vel = grad_temp; - - // Loop over quadrature points and mesh dimension - const int num_point = boundary_lagrange_pressure_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - // Lagrange pressure - EXPECT_DOUBLE_EQ(phi, - fieldValue(boundary_lagrange_pressure_result, 0, qp)); - - // Temperature - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(u_0 + u_1, - fieldValue(boundary_temperature_result, 0, qp)); - } - - // Loop over mesh dimension to assert gradient vectors - for (int d = 0; d < num_space_dim; ++d) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_velocity[d]); - EXPECT_DOUBLE_EQ(vel[d], - fieldValue(boundary_velocity_d_result, 0, qp)); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_grad_velocity[vel_dim]); - - EXPECT_DOUBLE_EQ( - grad_vel[vel_dim][d], - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (build_temp_equ) - { - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - grad_temp[d], - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = u_0 - u_1; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - wf_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D incompressible isothermal wall function -TEST_P(EvaluationTest, isothermalresidual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible isothermal wall function -TEST_P(EvaluationTest, isothermalresidual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -TEST_P(EvaluationTest, isothermaljacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D incompressible wall function -TEST_P(EvaluationTest, residual2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian2D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D incompressible wall function -TEST_P(EvaluationTest, residual3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -TEST_P(EvaluationTest, jacobian3D) -{ - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval(true, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleDirichlet.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleDirichlet.cpp deleted file mode 100644 index 6ba34ee..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleDirichlet.cpp +++ /dev/null @@ -1,404 +0,0 @@ -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleDirichlet.hpp" -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _phi, _u0, _u1, _u2; - ContinuityModel _continuity_model; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double phi, - const double u0, - const double u1, - const double u2, - const ContinuityModel continuity_model) - : _phi(phi) - , _u0(u0) - , _u1(u1) - , _u2(u2) - , _continuity_model(continuity_model) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - this->setName( - "Time Transient Incompressible Dirichlet Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _lagrange_pressure.deep_copy(_phi); - _grad_velocity_0.deep_copy(_u0); - _grad_velocity_1.deep_copy(_u1); - _grad_velocity_2.deep_copy(_u2); - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure.deep_copy(_u0 - _u1); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const Kokkos::Array time_values, - const Kokkos::Array final_values, - const Kokkos::Array init_values, - const Kokkos::Array exp_fields, - const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int num_grad_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Create dependencies - const double phi = 3.0; - const double u0 = final_values[0]; - const double u1 = final_values[1]; - const double u2 = num_space_dim == 3 ? final_values[2] : 0.0; - const double u0_init = init_values[0]; - const double u1_init = init_values[1]; - const double u2_init = num_space_dim == 3 ? init_values[2] : -1.0; - const double time_init = time_values[0]; - const double time_final = time_values[1]; - const double time = time_values[2]; - - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, phi, u0, u1, u2, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Equation of state - const bool build_temp_equ = false; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("velocity_0", u0); - bc_params.set("velocity_1", u1); - if (num_space_dim == 3) - bc_params.set("velocity_2", u2); - if (u0_init > 0.0) - bc_params.set("velocity_0_init", u0_init); - if (u1_init > 0.0) - bc_params.set("velocity_1_init", u1_init); - if (u2_init > 0.0) - bc_params.set("velocity_2_init", u2_init); - if (time_init > 0.0) - bc_params.set("Time Initial", time_init); - if (time_final > 0.0) - bc_params.set("Time Final", time_final); - - // Create dirichlet evaluator. - auto dirichlet_eval = Teuchos::rcp( - new BoundaryCondition::IncompressibleDirichlet( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(dirichlet_eval); - - // Add required test fields. - test_fixture.registerTestField( - dirichlet_eval->_boundary_lagrange_pressure); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - test_fixture.registerTestField( - dirichlet_eval->_boundary_velocity[vel_dim]); - test_fixture.registerTestField( - dirichlet_eval->_boundary_grad_velocity[vel_dim]); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - dirichlet_eval->_boundary_grad_lagrange_pressure); - } - - // Set time - test_fixture.setTime(time); - - // Evaluate time transient dirichlet BC. - test_fixture.evaluate(); - - // Check the time transient dirichlet BC. - const auto boundary_phi_result = test_fixture.getTestFieldData( - dirichlet_eval->_boundary_lagrange_pressure); - - const int num_point = boundary_phi_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(phi, fieldValue(boundary_phi_result, 0, qp)); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - dirichlet_eval->_boundary_velocity[vel_dim]); - EXPECT_DOUBLE_EQ(exp_fields[vel_dim], - fieldValue(boundary_velocity_d_result, 0, qp)); - } - - for (int d = 0; d < num_grad_dim; ++d) - { - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - // The gradients are initialized with entries from - // `final_values`. - const double exp_val = final_values[vel_dim]; - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - dirichlet_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = final_values[0] - final_values[1]; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - dirichlet_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D: time transient dirichlet - steady -TEST_P(EvaluationTest, steadyresidual2D) -{ - const Kokkos::Array time_values = {-0.5, -2.0, 3.0}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {-1.0, -1.2}; - const Kokkos::Array exp_fields = {2.0, 3.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -TEST_P(EvaluationTest, steadyjacobian2D) -{ - const Kokkos::Array time_values = {-0.5, -2.0, 3.0}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {-1.0, -1.2}; - const Kokkos::Array exp_fields = {2.0, 3.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient dirichlet - time > time_final -TEST_P(EvaluationTest, timefinalresidual2D) -{ - const Kokkos::Array time_values = {0.5, 2.0, 3.0}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 1.2}; - const Kokkos::Array exp_fields = {2.0, 3.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -TEST_P(EvaluationTest, timefinaljacobian2D) -{ - const Kokkos::Array time_values = {0.5, 2.0, 3.0}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 1.2}; - const Kokkos::Array exp_fields = {2.0, 3.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient dirichlet - time < time_init -TEST_P(EvaluationTest, timeinitresidual2D) -{ - const Kokkos::Array time_values = {0.5, 1.0, 0.2}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 1.2}; - const Kokkos::Array exp_fields = {1.0, 1.2}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -TEST_P(EvaluationTest, timeinitjacobian2D) -{ - const Kokkos::Array time_values = {0.5, 1.0, 0.2}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 1.2}; - const Kokkos::Array exp_fields = {1.0, 1.2}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient dirichlet - time_init < time < -// time_final -TEST_P(EvaluationTest, timeintermediateresidual2D) -{ - const Kokkos::Array time_values = {1.0, 2.0, 1.5}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 2.0}; - const Kokkos::Array exp_fields = {1.5, 2.5}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -TEST_P(EvaluationTest, timeintermediatejacobian2D) -{ - const Kokkos::Array time_values = {1.0, 2.0, 1.5}; - const Kokkos::Array final_values = {2.0, 3.0}; - const Kokkos::Array init_values = {1.0, 2.0}; - const Kokkos::Array exp_fields = {1.5, 2.5}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D: time transient dirichlet - time_init < time < -// time_final -TEST_P(EvaluationTest, timeintermediateresidual3D) -{ - const Kokkos::Array time_values = {1.0, 2.0, 1.5}; - const Kokkos::Array final_values = {2.0, 3.0, 4.0}; - const Kokkos::Array init_values = {1.0, 2.0, 3.0}; - const Kokkos::Array exp_fields = {1.5, 2.5, 3.5}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -TEST_P(EvaluationTest, timeintermediatejacobian3D) -{ - const Kokkos::Array time_values = {1.0, 2.0, 1.5}; - const Kokkos::Array final_values = {2.0, 3.0, 4.0}; - const Kokkos::Array init_values = {1.0, 2.0, 3.0}; - const Kokkos::Array exp_fields = {1.5, 2.5, 3.5}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, final_values, init_values, exp_fields, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleRotatingWall.cpp b/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleRotatingWall.cpp deleted file mode 100644 index a76464f..0000000 --- a/src/incompressible_solver/boundary_conditions/unit_test/tstTimeTransientIncompressibleRotatingWall.cpp +++ /dev/null @@ -1,449 +0,0 @@ -#include "incompressible_solver/boundary_conditions/VertexCFD_BoundaryState_IncompressibleRotatingWall.hpp" -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _lagrange_pressure; - PHX::MDField - _grad_velocity_0; - PHX::MDField - _grad_velocity_1; - PHX::MDField - _grad_velocity_2; - - bool _build_tmp_equ; - ContinuityModel _continuity_model; - - PHX::MDField - _grad_temperature; - PHX::MDField - _grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const bool build_tmp_equ, - const ContinuityModel continuity_model) - : _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _grad_velocity_0("GRAD_velocity_0", ir.dl_vector) - , _grad_velocity_1("GRAD_velocity_1", ir.dl_vector) - , _grad_velocity_2("GRAD_velocity_2", ir.dl_vector) - , _build_tmp_equ(build_tmp_equ) - , _continuity_model(continuity_model) - , _grad_temperature("GRAD_temperature", ir.dl_vector) - , _grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(_lagrange_pressure); - this->addEvaluatedField(_grad_velocity_0); - this->addEvaluatedField(_grad_velocity_1); - this->addEvaluatedField(_grad_velocity_2); - - if (build_tmp_equ) - this->addEvaluatedField(_grad_temperature); - if (_continuity_model == ContinuityModel::EDAC) - this->addEvaluatedField(_grad_lagrange_pressure); - - this->setName( - "Time Transient Incompressible Rotating Wall Unit Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - // Initialize pressure - _lagrange_pressure.deep_copy(0.4); - - // Initialize gradients - Kokkos::parallel_for( - "incompressible rotating wall test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _lagrange_pressure.extent(1); - const int num_space_dim = _grad_velocity_0.extent(2); - - // Loop over quadrature points and mesh dimension - for (int qp = 0; qp < num_point; ++qp) - { - for (int d = 0; d < num_space_dim; d++) - { - const int dqp = (d + 1 + num_space_dim) * (qp + 1); - _grad_velocity_0(c, qp, d) = 0.1 * dqp; - _grad_velocity_1(c, qp, d) = 0.2 * dqp; - if (num_space_dim == 3) - _grad_velocity_2(c, qp, d) = 0.3 * dqp; - - if (_build_tmp_equ) - _grad_temperature(c, qp, d) = 325 * dqp; - - if (_continuity_model == ContinuityModel::EDAC) - _grad_lagrange_pressure(c, qp, d) = 0.4; - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const Kokkos::Array time_values, - const Kokkos::Array init_values, - const Kokkos::Array exp_fields, - const bool build_temp_equ, - const ContinuityModel continuity_model) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int num_grad_dim = num_space_dim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - std::string continuity_model_name = ""; - switch (continuity_model) - { - case (ContinuityModel::AC): - continuity_model_name = "AC"; - break; - case (ContinuityModel::EDAC): - continuity_model_name = "EDAC"; - break; - } - - // Create dependencies - const double angular_velocity = 2.0; - const double angular_velocity_init = init_values[0]; - const double time_init = time_values[0]; - const double time_final = time_values[1]; - const double time = time_values[2]; - - // Initialize values - const double T_wall - = build_temp_equ ? 325 : std::numeric_limits::quiet_NaN(); - - // Set non-trivial quadrature points to avoid x = y - test_fixture.int_values->ip_coordinates(0, 0, 0) = 0.7375; - test_fixture.int_values->ip_coordinates(0, 0, 1) = 0.9775; - - // Initialize dependecy evaluator - const auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, build_temp_equ, continuity_model)); - test_fixture.registerEvaluator(dep_eval); - - // Thermophysical properties - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - bc_params.set("Angular Velocity", angular_velocity); - if (init_values[0] > 0.0) - bc_params.set("Angular Velocity Initial", angular_velocity_init); - if (time_init > 0.0) - bc_params.set("Time Initial", time_init); - if (time_final > 0.0) - bc_params.set("Time Final", time_final); - if (build_temp_equ) - bc_params.set("Wall Temperature", T_wall); - - // Create evaluator. - auto isotherm_eval = Teuchos::rcp( - new BoundaryCondition::IncompressibleRotatingWall( - *test_fixture.ir, fluid_prop, bc_params, continuity_model_name)); - test_fixture.registerEvaluator(isotherm_eval); - - // Add required test fields. - test_fixture.registerTestField( - isotherm_eval->_boundary_lagrange_pressure); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - test_fixture.registerTestField( - isotherm_eval->_boundary_velocity[vel_dim]); - test_fixture.registerTestField( - isotherm_eval->_boundary_grad_velocity[vel_dim]); - } - - if (build_temp_equ) - { - test_fixture.registerTestField( - isotherm_eval->_boundary_temperature); - test_fixture.registerTestField( - isotherm_eval->_boundary_grad_temperature); - } - - if (continuity_model == ContinuityModel::EDAC) - { - test_fixture.registerTestField( - isotherm_eval->_boundary_grad_lagrange_pressure); - } - - // Set time - test_fixture.setTime(time); - - // Evaluate - test_fixture.evaluate(); - - // Get field - const auto boundary_phi_result = test_fixture.getTestFieldData( - isotherm_eval->_boundary_lagrange_pressure); - - // Assert variables and gradients at each quadrature points - const int num_point = boundary_phi_result.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.4, fieldValue(boundary_phi_result, 0, qp)); - - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_velocity_d_result - = test_fixture.getTestFieldData( - isotherm_eval->_boundary_velocity[vel_dim]); - EXPECT_DOUBLE_EQ(exp_fields[vel_dim], - fieldValue(boundary_velocity_d_result, 0, qp)); - } - - if (build_temp_equ) - { - const auto boundary_temperature_result - = test_fixture.getTestFieldData( - isotherm_eval->_boundary_temperature); - EXPECT_DOUBLE_EQ(T_wall, - fieldValue(boundary_temperature_result, 0, qp)); - } - - for (int d = 0; d < num_grad_dim; ++d) - { - const int dqp = (d + 1 + num_space_dim) * (qp + 1); - for (int vel_dim = 0; vel_dim < num_space_dim; ++vel_dim) - { - const auto boundary_grad_velocity_d_result - = test_fixture.getTestFieldData( - isotherm_eval->_boundary_grad_velocity[vel_dim]); - EXPECT_DOUBLE_EQ( - (vel_dim + 1) * 0.1 * dqp, - fieldValue(boundary_grad_velocity_d_result, 0, qp, d)); - - if (build_temp_equ) - { - const auto boundary_grad_temperature_result - = test_fixture.getTestFieldData( - isotherm_eval->_boundary_grad_temperature); - EXPECT_DOUBLE_EQ( - T_wall * dqp, - fieldValue(boundary_grad_temperature_result, 0, qp, d)); - } - - if (continuity_model == ContinuityModel::EDAC) - { - const double exp_val = 0.4; - const auto boundary_grad_lagrange_pressure_result - = test_fixture.getTestFieldData( - isotherm_eval->_boundary_grad_lagrange_pressure); - EXPECT_DOUBLE_EQ( - exp_val, - fieldValue( - boundary_grad_lagrange_pressure_result, 0, qp, d)); - } - } - } - } -} - -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto continuity_model = info.param; - switch (continuity_model) - { - case (ContinuityModel::AC): - return "AC"; - case (ContinuityModel::EDAC): - return "EDAC"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// 2-D: time transient incompressible rotating wall - steady -TEST_P(EvaluationTest, steadyresidual2D) -{ - const Kokkos::Array time_values = {-0.5, -3.0, 3.0}; - const Kokkos::Array init_values = {-1.0}; - const Kokkos::Array exp_fields = {-1.955, 1.475}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -TEST_P(EvaluationTest, steadyjacobian2D) -{ - const Kokkos::Array time_values = {-0.5, -3.0, 3.0}; - const Kokkos::Array init_values = {-1.0}; - const Kokkos::Array exp_fields = {-1.955, 1.475}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient incompressible rotating wall - time > time_final -TEST_P(EvaluationTest, timefinalresidual2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 3.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.955, 1.475}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -TEST_P(EvaluationTest, timefinaljacobian2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 3.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.955, 1.475}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient incompressible rotating wall - time < time_init -TEST_P(EvaluationTest, timeinitresidual2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 0.2}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-0.9775, 0.7375}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -TEST_P(EvaluationTest, timeinitjacobian2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 0.2}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-0.9775, 0.7375}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 2-D: time transient incompressible rotating wall - time_init < time < -// time_final -TEST_P(EvaluationTest, timeintermediateresidual2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 1.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.3685, 1.0325}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -TEST_P(EvaluationTest, timeintermediatejacobian2D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 1.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.3685, 1.0325}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -//---------------------------------------------------------------------------// -// 3-D: time transient incompressible rotating wall - time_init < time < -// time_final -TEST_P(EvaluationTest, timeintermediateresidual3D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 1.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.3685, 1.0325, 0.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -TEST_P(EvaluationTest, timeintermediatejacobian3D) -{ - const Kokkos::Array time_values = {0.5, 3.0, 1.5}; - const Kokkos::Array init_values = {1.0}; - const Kokkos::Array exp_fields = {-1.3685, 1.0325, 0.0}; - ContinuityModel continuity_model; - continuity_model = GetParam(); - testEval( - time_values, init_values, exp_fields, false, continuity_model); -} - -//---------------------------------------------------------------------------// -// Generate test suite with continuity models -INSTANTIATE_TEST_SUITE_P(ContinuityModelType, - EvaluationTest, - testing::Values(ContinuityModel::AC, - ContinuityModel::EDAC), - EvaluationTest::ParamNameGenerator{}); - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.cpp deleted file mode 100644 index eaa5950..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleBuoyancySource.hpp" -#include "VertexCFD_Closure_IncompressibleBuoyancySource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleBuoyancySource) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp deleted file mode 100644 index dd1e004..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEBUOYANCYSOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEBUOYANCYSOURCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Thermal buoyancy source evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleBuoyancySource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleBuoyancySource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _buoyancy_continuity_source; - PHX::MDField _buoyancy_energy_source; - - Kokkos::Array, - num_space_dim> - _buoyancy_momentum_source; - - private: - PHX::MDField _temperature; - double _beta_T; - double _T_ref; - Kokkos::Array _gravity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEBUOYANCYSOURCE_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource_impl.hpp deleted file mode 100644 index 4f21b85..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource_impl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_BUOYANCYSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_BUOYANCYSOURCE_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleBuoyancySource:: - IncompressibleBuoyancySource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _buoyancy_continuity_source("BUOYANCY_SOURCE_continuity", ir.dl_scalar) - , _buoyancy_energy_source("BUOYANCY_SOURCE_energy", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) -{ - const auto gravity = user_params.get>("Gravity"); - for (int dim = 0; dim < num_space_dim; ++dim) - { - _gravity[dim] = gravity[dim]; - } - - // Evaluated fields - this->addEvaluatedField(_buoyancy_continuity_source); - this->addEvaluatedField(_buoyancy_energy_source); - - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _buoyancy_momentum_source, - "BUOYANCY_SOURCE_" - "momentum_"); - - _beta_T = fluid_prop.expansionCoefficient(); - _T_ref = fluid_prop.referenceTemperature(); - - // Dependent fields - this->addDependentField(_temperature); - - this->setName("Incompressible Buoyancy Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBuoyancySource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleBuoyancySource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _buoyancy_continuity_source.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _buoyancy_continuity_source(cell, point) = 0.0; - _buoyancy_energy_source(cell, point) = 0.0; - - for (int mom_dim = 0; mom_dim < num_space_dim; ++mom_dim) - { - _buoyancy_momentum_source[mom_dim](cell, point) - = -_beta_T * _gravity[mom_dim] - * (_temperature(cell, point) - _T_ref); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_BUOYANCYSOURCE_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.cpp deleted file mode 100644 index 32817f2..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleConstantSource.hpp" -#include "VertexCFD_Closure_IncompressibleConstantSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleConstantSource) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp deleted file mode 100644 index b960cc4..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLECONSTANTSOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLECONSTANTSOURCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension constant source evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleConstantSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleConstantSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _continuity_source; - Kokkos::Array, - num_space_dim> - _momentum_source; - PHX::MDField _energy_source; - - private: - bool _solve_temp; - Kokkos::Array _mom_input_source; - double _energy_input_source; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLECONSTANTSOURCE_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource_impl.hpp deleted file mode 100644 index ececfcf..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource_impl.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_CONSTANTSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_CONSTANTSOURCE_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleConstantSource:: - IncompressibleConstantSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _continuity_source("CONSTANT_SOURCE_continuity", ir.dl_scalar) - , _energy_source("CONSTANT_SOURCE_energy", ir.dl_scalar) - , _solve_temp(fluid_prop.solveTemperature()) -{ - const auto mom_input_source - = user_params.get>("Momentum Source"); - for (int dim = 0; dim < num_space_dim; ++dim) - _mom_input_source[dim] = mom_input_source[dim]; - - // Evaluated fields - this->addEvaluatedField(_continuity_source); - - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _momentum_source, - "CONSTANT_SOURCE_" - "momentum_"); - - if (_solve_temp) - { - _energy_input_source = user_params.get("Energy Source"); - this->addEvaluatedField(_energy_source); - } - - // Dependent fields - this->setName("Incompressible Constant Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleConstantSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleConstantSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _continuity_source.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _continuity_source(cell, point) = 0.0; - - for (int mom_dim = 0; mom_dim < num_space_dim; ++mom_dim) - _momentum_source[mom_dim](cell, point) - = _mom_input_source[mom_dim]; - if (_solve_temp) - _energy_source(cell, point) = _energy_input_source; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_CONSTANTSOURCE_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.cpp deleted file mode 100644 index 45d0c20..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleConvectiveFlux.hpp" -#include "VertexCFD_Closure_IncompressibleConvectiveFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleConvectiveFlux) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp deleted file mode 100644 index 76bd645..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension convective flux evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleConvectiveFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleConvectiveFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix = "", - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _continuity_flux; - PHX::MDField - _energy_flux; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _momentum_flux; - - private: - PHX::MDField _lagrange_pressure; - PHX::MDField _temperature; - Kokkos::Array, - num_space_dim> - _velocity; - - double _rho; - double _rhoCp; - bool _solve_temp; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux_impl.hpp deleted file mode 100644 index b3781cf..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux_impl.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleConvectiveFlux:: - IncompressibleConvectiveFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix, - const std::string& field_prefix) - : _continuity_flux(flux_prefix + "CONVECTIVE_FLUX_continuity", ir.dl_vector) - , _energy_flux(flux_prefix + "CONVECTIVE_FLUX_energy", ir.dl_vector) - , _lagrange_pressure(field_prefix + "lagrange_pressure", ir.dl_scalar) - , _temperature(field_prefix + "temperature", ir.dl_scalar) - , _rho(fluid_prop.constantDensity()) - , _rhoCp(std::numeric_limits::quiet_NaN()) - , _solve_temp(fluid_prop.solveTemperature()) -{ - // Evaluated fields - this->addEvaluatedField(_continuity_flux); - - Utils::addEvaluatedVectorField(*this, ir.dl_vector, _momentum_flux, - flux_prefix + "CONVECTIVE_FLUX_" - "momentum_"); - - if (_solve_temp) - { - _rhoCp = fluid_prop.constantHeatCapacity(); - this->addEvaluatedField(_energy_flux); - } - - // Dependent fields - Utils::addDependentVectorField( - *this, ir.dl_scalar, _velocity, field_prefix + "velocity_"); - this->addDependentField(_lagrange_pressure); - - if (_solve_temp) - this->addDependentField(_temperature); - - this->setName("Incompressible Convective Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleConvectiveFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleConvectiveFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _continuity_flux.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto vel_dim = _velocity[dim](cell, point); - _continuity_flux(cell, point, dim) = _rho * vel_dim; - - for (int mom_dim = 0; mom_dim < num_space_dim; ++mom_dim) - { - _momentum_flux[mom_dim](cell, point, dim) - = _rho * vel_dim * _velocity[mom_dim](cell, point); - if (mom_dim == dim) - { - _momentum_flux[mom_dim](cell, point, dim) - += _lagrange_pressure(cell, point); - } - } - - if (_solve_temp) - { - _energy_flux(cell, point, dim) - = _rhoCp * vel_dim * _temperature(cell, point); - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLECONVECTIVEFLUX_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp deleted file mode 100644 index 0524502..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute error norms between exact solution and numerical solutions -//---------------------------------------------------------------------------// -template -class IncompressibleErrorNorms : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleErrorNorms(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _L1_error_continuity; - Kokkos::Array, - num_space_dim> - _L1_error_momentum; - PHX::MDField _L1_error_energy; - - PHX::MDField _L2_error_continuity; - Kokkos::Array, - num_space_dim> - _L2_error_momentum; - PHX::MDField _L2_error_energy; - - PHX::MDField _volume; - - private: - PHX::MDField - _exact_lagrange_pressure; - Kokkos::Array, - num_space_dim> - _exact_velocity; - PHX::MDField _exact_temperature; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField _temperature; - - bool _use_temp; -}; - -//---------------------------------------------------------------------------// - -} // namespace ClosureModel -} // end namespace VertexCFD - -#include "VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp" - -#endif // VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp deleted file mode 100644 index e872965..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms_impl.hpp +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleErrorNorms::IncompressibleErrorNorms( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params) - : _L1_error_continuity("L1_Error_continuity", ir.dl_scalar) - , _L1_error_energy("L1_Error_energy", ir.dl_scalar) - , _L2_error_continuity("L2_Error_continuity", ir.dl_scalar) - , _L2_error_energy("L2_Error_energy", ir.dl_scalar) - , _volume("volume", ir.dl_scalar) - , _exact_lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _exact_temperature("Exact_temperature", ir.dl_scalar) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _temperature("temperature", ir.dl_scalar) -{ - // Temperature boolean - _use_temp = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - - // exact solution - this->addDependentField(_exact_lagrange_pressure); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _exact_velocity, "Exact_velocity_"); - if (_use_temp) - this->addDependentField(_exact_temperature); - - // numerical solution - this->addDependentField(_lagrange_pressure); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - if (_use_temp) - this->addDependentField(_temperature); - - // error between exact and numerical solution - this->addEvaluatedField(_L1_error_continuity); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _L1_error_momentum, "L1_Error_momentum_"); - this->addEvaluatedField(_L2_error_continuity); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _L2_error_momentum, "L2_Error_momentum_"); - - if (_use_temp) - { - this->addEvaluatedField(_L1_error_energy); - this->addEvaluatedField(_L2_error_energy); - } - this->addEvaluatedField(_volume); - - this->setName("Incompressible Error Norms " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleErrorNorms::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleErrorNorms::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - using std::abs; - using std::pow; - - // L1/L2 error norms - _L1_error_continuity(cell, point) - = abs(_lagrange_pressure(cell, point) - - _exact_lagrange_pressure(cell, point)); - - if (_use_temp) - { - _L1_error_energy(cell, point) - = abs(_temperature(cell, point) - - _exact_temperature(cell, point)); - } - - _L2_error_continuity(cell, point) - = pow(_lagrange_pressure(cell, point) - - _exact_lagrange_pressure(cell, point), - 2); - - if (_use_temp) - { - _L2_error_energy(cell, point) = pow( - _temperature(cell, point) - _exact_temperature(cell, point), - 2); - } - - for (int i = 0; i < num_space_dim; ++i) - { - _L1_error_momentum[i](cell, point) - = abs(_velocity[i](cell, point) - - _exact_velocity[i](cell, point)); - _L2_error_momentum[i](cell, point) = pow( - _velocity[i](cell, point) - _exact_velocity[i](cell, point), - 2); - } - - _volume(cell, point) = 1.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_INCOMPRESSIBLEERRORNORMS_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.cpp deleted file mode 100644 index 859ac96..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleLiftDrag.hpp" -#include "VertexCFD_Closure_IncompressibleLiftDrag_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleLiftDrag) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp deleted file mode 100644 index 7ef16ec..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Aerodyanmic force evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleLiftDrag : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleLiftDrag( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _shear_tensor; - Kokkos::Array, - num_space_dim> - _viscous_force; - Kokkos::Array, - num_space_dim> - _pressure_force; - Kokkos::Array, - num_space_dim> - _total_force; - - private: - PHX::MDField - _normals; - PHX::MDField _lagrange_pressure; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _nu; - double _rho; - bool _use_compressible_formula; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag_impl.hpp deleted file mode 100644 index a6e4590..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag_impl.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleLiftDrag::IncompressibleLiftDrag( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _normals("Side Normal", ir.dl_vector) - , _lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _rho(fluid_prop.constantDensity()) - , _use_compressible_formula(user_params.isType("Compressible " - "Formula") - ? user_params.get("Compressible " - "Formula") - : false) -{ - // Add evaluated field - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _shear_tensor, "shear_tensor_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _viscous_force, "viscous_force_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _pressure_force, "pressure_force_"); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _total_force, "total_force_"); - - // Add dependent field - this->addDependentField(_normals); - this->addDependentField(_lagrange_pressure); - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Incompressible Lift/Drag " + std::to_string(num_space_dim) - + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLiftDrag::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLiftDrag::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Calculate wall shear tensor (grad.U + transpose(grad.U)) - for (int i = 0; i < num_space_dim; ++i) - { - _shear_tensor[i](cell, point) = 0; - for (int j = 0; j < num_space_dim; ++j) - { - _shear_tensor[i](cell, point) - += (_grad_velocity[j](cell, point, i) - + _grad_velocity[i](cell, point, j)) - * _normals(cell, point, j); - // If compressible formula div.U != 0 hence calculate - // deviatoric part - if (_use_compressible_formula) - { - _shear_tensor[i](cell, point) - -= 2.0 * _grad_velocity[j](cell, point, j) - / num_space_dim * _normals(cell, point, i); - } - } - _pressure_force[i](cell, point) - = _lagrange_pressure(cell, point) - * _normals(cell, point, i); - _viscous_force[i](cell, point) - = -_rho * _nu * _shear_tensor[i](cell, point); - _total_force[i](cell, point) - = _viscous_force[i](cell, point) - + _pressure_force[i](cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELIFTDRAG_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.cpp deleted file mode 100644 index 7453a4d..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp" -#include "VertexCFD_Closure_IncompressibleLocalTimeStepSize_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleLocalTimeStepSize) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp deleted file mode 100644 index 480cee2..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Compute local time step size based on the mesh size and maximum eigenvalue. -// The time step size used by the solver and based on the CFL condition is -// computed in the observer -// 'VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp' -//---------------------------------------------------------------------------// -template -class IncompressibleLocalTimeStepSize - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleLocalTimeStepSize(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData d) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _local_dt; - - private: - PHX::MDField - _element_length; - Kokkos::Array, - num_space_dim> - _velocity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize_impl.hpp deleted file mode 100644 index 5b51775..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize_impl.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_IMPL_HPP - -#include - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleLocalTimeStepSize:: - IncompressibleLocalTimeStepSize(const panzer::IntegrationRule& ir) - : _local_dt("local_dt", ir.dl_scalar) - , _element_length("element_length", ir.dl_vector) -{ - // Add evaluated field - this->addEvaluatedField(_local_dt); - - // Add dependent fields - this->addDependentField(_element_length); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - - this->setName("Incompressible Local Time Step Size"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLocalTimeStepSize::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLocalTimeStepSize::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _local_dt.extent(1); - const int num_grad_dim = _element_length.extent(2); - - const double tol = 1.0e-8; - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - scalar_type one_over_dt = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - one_over_dt += SmoothMath::abs(_velocity[dim](cell, point), tol) - / _element_length(cell, point, dim); - } - _local_dt(cell, point) = 1.0 / one_over_dt; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLELOCALTIMESTEPSIZE_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.cpp deleted file mode 100644 index 6826921..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp" -#include "VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressiblePlanarPoiseuilleExact) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp deleted file mode 100644 index 4fa2c48..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Exact solution for planar Poiseuille flow with viscous dissipation -//---------------------------------------------------------------------------// -template -class IncompressiblePlanarPoiseuilleExact - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressiblePlanarPoiseuilleExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _temperature; - PHX::MDField _lagrange_pressure; - Kokkos::Array, num_space_dim> - _velocity; - - private: - double _nu; - double _rho; - double _k; - double _cp; - double _h_min; - double _h_max; - double _T_l; - double _T_u; - double _S_u; - - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact_impl.hpp deleted file mode 100644 index 017d185..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact_impl.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressiblePlanarPoiseuilleExact:: - IncompressiblePlanarPoiseuilleExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _temperature("Exact_temperature", ir.dl_scalar) - , _lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _rho(fluid_prop.constantDensity()) - , _k(fluid_prop.constantThermalConductivity()) - , _cp(fluid_prop.constantHeatCapacity()) - , _ir_degree(ir.cubature_degree) -{ - // Read parameters from planar Poiseuille sublist - const Teuchos::ParameterList pp_list - = user_params.sublist("Planar Poiseuille Exact"); - - _h_min = pp_list.get("H min"); - _h_max = pp_list.get("H max"); - _T_l = pp_list.get("Lower wall temperature"); - _T_u = pp_list.get("Upper wall temperature"); - _S_u = pp_list.get("Momentum source"); - - // Evaluated fields - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_lagrange_pressure); - - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "Exact_velocity_"); - - this->setName("Exact Solution Planar Poiseuille"); -} - -//---------------------------------------------------------------------------// -template -void IncompressiblePlanarPoiseuilleExact:: - postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressiblePlanarPoiseuilleExact:: - evaluateFields(typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressiblePlanarPoiseuilleExact::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _temperature.extent(1); - - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Note: an analytical solution is only available for the velocity - // and temperature fields - _lagrange_pressure(cell, point) = 0.0; - - // Get geometric and thermal parameters - const double H = (_h_max - _h_min) / 2.0; - const double y = _ip_coords(cell, point, 1); - const double Pr = _cp * _rho * _nu / _k; - const double dT = _T_l - _T_u; - const double U = _S_u * H * H / 3.0 / _nu; - const double E = U * U / _cp / dT; - - // Calculate excess temperature - const double T_star = 0.5 * (1.0 - (y / H)) - + 3.0 / 4.0 * Pr * E - * (1.0 - pow(y / H, 4.0)); - // Back out exact temperature - _temperature(cell, point) = T_star * dT + _T_u; - - // Calculate exact velocity - _velocity[0](cell, point) = 3.0 / 2.0 * U * (1.0 - pow(y / H, 2.0)); - _velocity[1](cell, point) = 0.0; - - if (num_space_dim == 3) - _velocity[2](cell, point) = 0.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEPLANARPOISEUILLEEXACT_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.cpp deleted file mode 100644 index 3191dea..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp" -#include "VertexCFD_Closure_IncompressibleRotatingAnnulusExact_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleRotatingAnnulusExact) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp deleted file mode 100644 index ab65d7f..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Exact solution for rotating annulus with viscous heating -//---------------------------------------------------------------------------// -template -class IncompressibleRotatingAnnulusExact - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleRotatingAnnulusExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _temperature; - PHX::MDField _lagrange_pressure; - Kokkos::Array, num_space_dim> - _velocity; - - private: - double _nu; - double _rho; - double _k; - double _ro; - double _ri; - double _kappa; - double _omega; - double _To; - double _Ti; - double _N; - - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact_impl.hpp deleted file mode 100644 index ddd1deb..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact_impl.hpp +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleRotatingAnnulusExact:: - IncompressibleRotatingAnnulusExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _temperature("Exact_temperature", ir.dl_scalar) - , _lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _rho(fluid_prop.constantDensity()) - , _k(fluid_prop.constantThermalConductivity()) - , _ro(user_params.get("Outer radius")) - , _ri(user_params.get("Inner radius")) - , _kappa(_ri / _ro) - , _omega(user_params.get("Angular velocity")) - , _To(user_params.get("Outer wall temperature")) - , _Ti(user_params.get("Inner wall temperature")) - , _ir_degree(ir.cubature_degree) -{ - using std::pow; - - // Evaluated fields - this->addEvaluatedField(_temperature); - this->addEvaluatedField(_lagrange_pressure); - - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "Exact_velocity_"); - - // Brinkman number - _N = _nu * _rho * pow(_omega, 2.0) * pow(_ro, 2.0) / _k / (_To - _Ti) - * pow(_kappa, 4.0) / pow(1.0 - pow(_kappa, 2.0), 2.0); - - this->setName("Exact Solution Rotating Annulus"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingAnnulusExact:: - postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingAnnulusExact:: - evaluateFields(typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRotatingAnnulusExact::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _temperature.extent(1); - - using std::log; - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Note: an analytical solution is only available for the velocity - // and temperature fields - _lagrange_pressure(cell, point) = 0.0; - - // Get radius - const double x = _ip_coords(cell, point, 0); - const double y = _ip_coords(cell, point, 1); - const double r = sqrt(x * x + y * y); - const double ksi = r / _ro; - - // Calculate non-dimensional temperature - const double theta = (1.0 - log(ksi) / log(_kappa)) - + _N - * ((1 - 1 / (ksi * ksi)) - - (1 - 1 / (_kappa * _kappa)) - * log(ksi) / log(_kappa)); - - // Back out exact temperature - _temperature(cell, point) = theta * (_To - _Ti) + _Ti; - - // Tangential velocity - const double u_phi = _omega * _ro * _ro / (_ro * _ro - _ri * _ri) - * (r - (_ri * _ri / r)); - - // Exact velocity - _velocity[0](cell, point) = -u_phi * y / r; - _velocity[1](cell, point) = u_phi * x / r; - - if (num_space_dim == 3) - _velocity[2](cell, point) = 0.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEROTATINGANNULUSEXACT_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.cpp deleted file mode 100644 index ab73ed8..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleShearVariables.hpp" -#include "VertexCFD_Closure_IncompressibleShearVariables_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleShearVariables) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp deleted file mode 100644 index 40b8305..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension shear variables evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleShearVariables - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleShearVariables( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _tau_w; - PHX::MDField _u_tau; - - private: - PHX::MDField - _normals; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _nu; - double _rho; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables_impl.hpp deleted file mode 100644 index ea4ad20..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables_impl.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleShearVariables:: - IncompressibleShearVariables( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _tau_w("wall_shear_stress", ir.dl_scalar) - , _u_tau("friction_velocity", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - , _nu(fluid_prop.constantKinematicViscosity()) - , _rho(fluid_prop.constantDensity()) -{ - // Add evaluated field - this->addEvaluatedField(_tau_w); - this->addEvaluatedField(_u_tau); - - // Add dependent field - this->addDependentField(_normals); - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - this->setName("Incompressible Shear/Friction Variables " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleShearVariables::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleShearVariables::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _u_tau.extent(1); - const int num_grad_dim = _normals.extent(2); - using std::pow; - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Wall shear-stress - _tau_w(cell, point) = 0.0; - for (int i = 0; i < num_space_dim; ++i) - { - scalar_type sum = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - sum += _normals(cell, point, dim) - * _grad_velocity[i](cell, point, dim); - } - _tau_w(cell, point) += pow(sum, 2.0); - } - _tau_w(cell, point) - = sqrt(SmoothMath::max(_tau_w(cell, point), 1.0E-10, 0.0)); - _tau_w(cell, point) *= _nu; - - // Friction velocity - _u_tau(cell, point) = sqrt(_tau_w(cell, point)); - _tau_w(cell, point) *= _rho; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLESHEARVARIABLES_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.cpp deleted file mode 100644 index 6767b5e..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp" -#include "VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleTaylorGreenVortexExactSolution) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp deleted file mode 100644 index 0edc39d..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Taylor-Green vortex exact solution. -//---------------------------------------------------------------------------// -template -class IncompressibleTaylorGreenVortexExactSolution - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleTaylorGreenVortexExactSolution( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, num_space_dim> - _velocity; - - private: - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; - const double _nu; - double _Ft; - double _time; -}; - -//---------------------------------------------------------------------------// - -} // namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution_impl.hpp deleted file mode 100644 index 01cf147..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution_impl.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleTaylorGreenVortexExactSolution:: - IncompressibleTaylorGreenVortexExactSolution( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _ir_degree(ir.cubature_degree) - , _nu(fluid_prop.constantKinematicViscosity()) - , _Ft(0.0) -{ - this->addEvaluatedField(_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _velocity, "Exact_velocity_"); - - this->setName("Incompressible Taylor Green Vortex Exact Solution"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortexExactSolution:: - postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortexExactSolution:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - - using std::exp; - _Ft = exp(-2.0 * _nu * workset.time); - _time = workset.time; - - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortexExactSolution:: -operator()(const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _lagrange_pressure.extent(1); - - using std::cos; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - const double x = _ip_coords(cell, point, 0); - const double y = _ip_coords(cell, point, 1); - - _lagrange_pressure(cell, point) - = -0.25 * (cos(2.0 * x) + cos(2.0 * y)) * _Ft * _Ft; - _velocity[0](cell, point) = cos(x) * sin(y) * _Ft; - _velocity[1](cell, point) = -sin(x) * cos(y) * _Ft; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // VERTEXCFD_CLOSURE_INCOMPRESSIBLETAYLORGREENVORTEXEXACTSOLUTION_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.cpp deleted file mode 100644 index c9c8a23..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleTimeDerivative.hpp" -#include "VertexCFD_Closure_IncompressibleTimeDerivative_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleTimeDerivative) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp deleted file mode 100644 index b9d84d2..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class IncompressibleTimeDerivative - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleTimeDerivative( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData d) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _dqdt_continuity; - PHX::MDField _dqdt_energy; - Kokkos::Array, - num_space_dim> - _dqdt_momentum; - - private: - PHX::MDField - _dxdt_lagrange_pressure; - Kokkos::Array, - num_space_dim> - _dxdt_velocity; - PHX::MDField _dxdt_temperature; - - double _rho; - bool _solve_temp; - double _rhoCp; - double _beta; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative_impl.hpp deleted file mode 100644 index ec40b62..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative_impl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleTimeDerivative:: - IncompressibleTimeDerivative( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _dqdt_continuity("DQDT_continuity", ir.dl_scalar) - , _dqdt_energy("DQDT_energy", ir.dl_scalar) - , _dxdt_lagrange_pressure("DXDT_lagrange_pressure", ir.dl_scalar) - , _dxdt_temperature("DXDT_temperature", ir.dl_scalar) - , _rho(fluid_prop.constantDensity()) - , _solve_temp(fluid_prop.solveTemperature()) - , _rhoCp(fluid_prop.constantHeatCapacity()) - , _beta(fluid_prop.artificialCompressibility()) -{ - // Evaluated continuity - this->addEvaluatedField(_dqdt_continuity); - - // Dependent and evaluated velocity-based fields - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _dqdt_momentum, "DQDT_momentum_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _dxdt_velocity, "DXDT_velocity_"); - this->addDependentField(_dxdt_lagrange_pressure); - - // Dependent and evaluated temperature - if (_solve_temp) - { - this->addEvaluatedField(_dqdt_energy); - this->addDependentField(_dxdt_temperature); - } - - this->setName("Incompressible Time Derivative " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTimeDerivative::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTimeDerivative::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _dqdt_continuity.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _dqdt_continuity(cell, point) - = _dxdt_lagrange_pressure(cell, point) / _beta; - - for (int dim = 0; dim < num_space_dim; ++dim) - _dqdt_momentum[dim](cell, point) - = _rho * _dxdt_velocity[dim](cell, point); - - if (_solve_temp) - { - _dqdt_energy(cell, point) = _rhoCp - * _dxdt_temperature(cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLETIMEDERIVATIVE_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.cpp deleted file mode 100644 index b8584f2..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp" -#include "VertexCFD_Closure_IncompressibleVariableTimeDerivative_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleVariableTimeDerivative) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp deleted file mode 100644 index 4d7f230..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -// Time derivative term for incompressible turbulence variable equation -//---------------------------------------------------------------------------// -template -class IncompressibleVariableTimeDerivative - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleVariableTimeDerivative( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params); - - void evaluateFields(typename Traits::EvalData d) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - std::string _variable_name; - std::string _equation_name; - - public: - PHX::MDField _dqdt_var_eq; - - private: - PHX::MDField _dxdt_var; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative_impl.hpp deleted file mode 100644 index e25fdbb..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative_impl.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleVariableTimeDerivative:: - IncompressibleVariableTimeDerivative( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params) - : _variable_name(closure_params.get("Field Name")) - , _equation_name(closure_params.get("Equation Name")) - , _dqdt_var_eq("DQDT_" + _equation_name, ir.dl_scalar) - , _dxdt_var("DXDT_" + _variable_name, ir.dl_scalar) -{ - // Evaluated variable - this->addEvaluatedField(_dqdt_var_eq); - - // Dependent variable - this->addDependentField(_dxdt_var); - - // Closure model name - const int num_grad_dim = ir.spatial_dimension; - this->setName(_equation_name + " Incompressible Time Derivative " - + std::to_string(num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableTimeDerivative::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableTimeDerivative::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _dqdt_var_eq.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _dqdt_var_eq(cell, point) = _dxdt_var(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLETIMEDERIVATIVE_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.cpp deleted file mode 100644 index cceddbd..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleViscousFlux.hpp" -#include "VertexCFD_Closure_IncompressibleViscousFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleViscousFlux) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp deleted file mode 100644 index 569624d..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension viscous flux evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleViscousFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleViscousFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params, - const bool use_turbulence_model, - const std::string& flux_prefix = "", - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _continuity_flux; - PHX::MDField - _energy_flux; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _momentum_flux; - - private: - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - PHX::MDField - _grad_press; - PHX::MDField - _grad_temp; - PHX::MDField _nu_t; - PHX::MDField _kappa_t; - - double _rho; - double _nu; - double _kappa; - double _beta; - bool _solve_temp; - bool _use_turbulence_model; - std::string _continuity_model_name; - bool _is_edac; - double _rhoCp; - double _Pr_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux_impl.hpp deleted file mode 100644 index dbe5967..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux_impl.hpp +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleViscousFlux::IncompressibleViscousFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params, - const bool use_turbulence_model, - const std::string& flux_prefix, - const std::string& gradient_prefix) - : _continuity_flux(flux_prefix + "VISCOUS_FLUX_continuity", ir.dl_vector) - , _energy_flux(flux_prefix + "VISCOUS_FLUX_energy", ir.dl_vector) - , _grad_press(gradient_prefix + "GRAD_lagrange_pressure", ir.dl_vector) - , _grad_temp(gradient_prefix + "GRAD_temperature", ir.dl_vector) - , _nu_t(flux_prefix + "turbulent_eddy_viscosity", ir.dl_scalar) - , _rho(fluid_prop.constantDensity()) - , _nu(fluid_prop.constantKinematicViscosity()) - , _kappa(std::numeric_limits::quiet_NaN()) - , _beta(fluid_prop.artificialCompressibility()) - , _solve_temp(fluid_prop.solveTemperature()) - , _use_turbulence_model(use_turbulence_model) - , _continuity_model_name(user_params.isType("Continuity " - "Model") - ? user_params.get("Continuity " - "Model") - : "AC") - , _is_edac(_continuity_model_name == "EDAC" ? true : false) - , _rhoCp(fluid_prop.constantHeatCapacity()) - , _Pr_t(_solve_temp - ? (user_params.isType("Turbulent Prandtl Number") - ? user_params.get("Turbulent Prandtl Number") - : 0.85) - : std::numeric_limits::quiet_NaN()) -{ - // Add evaludated fields - this->addEvaluatedField(_continuity_flux); - Utils::addEvaluatedVectorField(*this, ir.dl_vector, _momentum_flux, - flux_prefix + "VISCOUS_FLUX_" - "momentum_"); - this->addEvaluatedField(_energy_flux); - - // Add dependent fields - if (_is_edac) - { - this->addDependentField(_grad_press); - } - if (_solve_temp) - { - _kappa = fluid_prop.constantThermalConductivity(); - this->addDependentField(_grad_temp); - } - - Utils::addDependentVectorField(*this, - ir.dl_vector, - _grad_velocity, - gradient_prefix + "GRAD_velocity_"); - - if (_use_turbulence_model) - { - this->addDependentField(_nu_t); - } - - this->setName("Incompressible Viscous Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleViscousFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleViscousFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _continuity_flux.extent(1); - const double ratio_kappa_t_nu_t = _rhoCp / _Pr_t; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Loop over spatial dimension - for (int i = 0; i < num_space_dim; ++i) - { - // Set stress tensor for EDAC continuity model - if (_is_edac) - { - _continuity_flux(cell, point, i) - = _rho * _nu * _grad_press(cell, point, i) / _beta; - } - // Set stress tensor to zero for AC continuity model - else - { - _continuity_flux(cell, point, i) = 0.0; - } - - // Temperature equation - if (_solve_temp) - { - _energy_flux(cell, point, i) - = _kappa * _grad_temp(cell, point, i); - if (_use_turbulence_model) - { - _energy_flux(cell, point, i) - += _nu_t(cell, point) * ratio_kappa_t_nu_t - * _grad_temp(cell, point, i); - } - } - - // Loop over velocity/momentum components - for (int j = 0; j < num_space_dim; ++j) - { - _momentum_flux[j](cell, point, i) - = _rho * _nu * _grad_velocity[j](cell, point, i); - if (_use_turbulence_model) - { - _momentum_flux[j](cell, point, i) - += _rho * _nu_t(cell, point) - * _grad_velocity[j](cell, point, i); - } - } - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSFLUX_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.cpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.cpp deleted file mode 100644 index 58914ab..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleViscousHeat.hpp" -#include "VertexCFD_Closure_IncompressibleViscousHeat_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleViscousHeat) diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp deleted file mode 100644 index 30c23a9..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSHEAT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSHEAT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Viscous heat source evaluation. -//---------------------------------------------------------------------------// -template -class IncompressibleViscousHeat - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleViscousHeat( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _viscous_heat_continuity_source; - PHX::MDField - _viscous_heat_energy_source; - - Kokkos::Array, - num_space_dim> - _viscous_heat_momentum_source; - - private: - const double _rho; - const double _nu; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVISCOUSHEAT_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat_impl.hpp deleted file mode 100644 index 62b6201..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat_impl.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_VISCOUSHEAT_IMPL_HPP -#define VERTEXCFD_CLOSURE_VISCOUSHEAT_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleViscousHeat::IncompressibleViscousHeat( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& gradient_prefix) - : _viscous_heat_continuity_source("VISCOUS_HEAT_continuity", ir.dl_scalar) - , _viscous_heat_energy_source("VISCOUS_HEAT_energy", ir.dl_scalar) - , _rho(fluid_prop.constantDensity()) - , _nu(fluid_prop.constantKinematicViscosity()) -{ - // Evaluated fields - this->addEvaluatedField(_viscous_heat_continuity_source); - this->addEvaluatedField(_viscous_heat_energy_source); - - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _viscous_heat_momentum_source, - "VISCOUS_HEAT_" - "momentum_"); - - // Dependent fields - Utils::addDependentVectorField(*this, - ir.dl_vector, - _grad_velocity, - gradient_prefix + "GRAD_velocity_"); - - this->setName("Incompressible Viscous Heat " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleViscousHeat::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleViscousHeat::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _viscous_heat_continuity_source.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _viscous_heat_continuity_source(cell, point) = 0.0; - - // Reset energy source to zero - _viscous_heat_energy_source(cell, point) = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - // Calculate deformation tensor component - const scalar_type e_ij - = 0.5 - * (_grad_velocity[j](cell, point, i) - + _grad_velocity[i](cell, point, j)); - - // Add contribution from each dimension to energy source - _viscous_heat_energy_source(cell, point) - += 2.0 * _rho * _nu * e_ij * e_ij; - } - - // No momentum contribution for this source term - _viscous_heat_momentum_source[i](cell, point) = 0.0; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_VISCOUSHEAT_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp deleted file mode 100644 index be02edb..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_HPP -#define VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class IncompressibleFactory -{ - public: - void buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& closure_params, - const bool use_turbulence_model, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators); -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_HPP diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian2d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian2d.cpp deleted file mode 100644 index f2a5d1a..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian3d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian3d.cpp deleted file mode 100644 index 79fdeeb..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Hessian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian2d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian2d.cpp deleted file mode 100644 index 5b122c0..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian3d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian3d.cpp deleted file mode 100644 index 68a76b8..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Jacobian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual2d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual2d.cpp deleted file mode 100644 index c2392e6..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual3d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual3d.cpp deleted file mode 100644 index 0961f5b..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Residual3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent2d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent2d.cpp deleted file mode 100644 index 0a23541..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent3d.cpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent3d.cpp deleted file mode 100644 index cd7d781..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_Tangent3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "VertexCFD_IncompressibleClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - IncompressibleFactory; diff --git a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_impl.hpp b/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_impl.hpp deleted file mode 100644 index fcda876..0000000 --- a/src/incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory_impl.hpp +++ /dev/null @@ -1,212 +0,0 @@ -#ifndef VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_IMPL_HPP -#define VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_IMPL_HPP - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleErrorNorms.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp" -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp" -#include "incompressible_solver/closure_models/VertexCFD_IncompressibleClosureModelFactory.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -void IncompressibleFactory::buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& /**closure_params**/, - const bool use_turbulence_model, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators) -{ - // Define local variables - constexpr int num_space_dim = NumSpaceDim; - Teuchos::RCP> eval; - - // Fluid properties - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_buoyancy_source - = user_params.isType("Build Buoyancy Source") - ? user_params.get("Build Buoyancy Source") - : false; - const bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Buoyancy Source", build_buoyancy_source); - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params - = FluidProperties::ConstantFluidProperties(fluid_prop_list); - - // Closure models - if (closure_type == "IncompressibleTimeDerivative") - { - auto eval = Teuchos::rcp( - new IncompressibleTimeDerivative( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleLiftDrag") - { - auto eval = Teuchos::rcp( - new IncompressibleLiftDrag( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleConvectiveFlux") - { - auto eval = Teuchos::rcp( - new IncompressibleConvectiveFlux( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleViscousFlux") - { - auto eval = Teuchos::rcp( - new IncompressibleViscousFlux( - *ir, - incompressible_fluidprop_params, - user_params, - use_turbulence_model)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleLocalTimeStepSize") - { - auto eval = Teuchos::rcp( - new IncompressibleLocalTimeStepSize(*ir)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleConstantSource") - { - auto eval = Teuchos::rcp( - new IncompressibleConstantSource( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleBuoyancySource") - { - auto eval = Teuchos::rcp( - new IncompressibleBuoyancySource( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleViscousHeat") - { - auto eval = Teuchos::rcp( - new IncompressibleViscousHeat( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleRotatingAnnulusExact") - { - auto eval = Teuchos::rcp( - new IncompressibleRotatingAnnulusExact( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressiblePlanarPoiseuilleExact") - { - auto eval = Teuchos::rcp( - new IncompressiblePlanarPoiseuilleExact( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleErrorNorm") - { - auto eval = Teuchos::rcp( - new IncompressibleErrorNorms( - *ir, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleTaylorGreenVortexExactSolution") - { - auto eval = Teuchos::rcp( - new IncompressibleTaylorGreenVortexExactSolution( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "IncompressibleShearVariables") - { - auto eval = Teuchos::rcp( - new IncompressibleShearVariables( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - // Initialize 'error_msg' with list of closure models for incompressible - // NS equations - error_msg += "IncompressibleBuoyancySource\n"; - error_msg += "IncompressibleConstantSource\n"; - error_msg += "IncompressibleConvectiveFlux\n"; - error_msg += "IncompressibleErrorNorm\n"; - error_msg += "IncompressibleLiftDrag\n"; - error_msg += "IncompressibleLocalTimeStepSize\n"; - error_msg += "IncompressiblePlanarPoiseuilleExact\n"; - error_msg += "IncompressibleRotatingAnnulusExact\n"; - error_msg += "IncompressibleShearVariables\n"; - error_msg += "IncompressibleTimeDerivative\n"; - error_msg += "IncompressibleViscousFlux\n"; - error_msg += "IncompressibleViscousHeat\n"; -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INCOMPRESSIBLECLOSUREMODELFACTORY_IMPL_HPP diff --git a/src/incompressible_solver/closure_models/unit_test/CMakeLists.txt b/src/incompressible_solver/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index 9a0675e..0000000 --- a/src/incompressible_solver/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - IncompressibleTimeDerivative - IncompressibleConvectiveFlux - IncompressibleLiftDrag - IncompressibleViscousFlux - IncompressibleViscousHeat - IncompressibleLocalTimeStepSize - IncompressibleConstantSource - IncompressibleBuoyancySource - IncompressibleRotatingAnnulusExact - IncompressiblePlanarPoiseuilleExact - IncompressibleTaylorGreenVortexExactSolution - IncompressibleErrorNorms - IncompressibleVariableTimeDerivative - IncompressibleShearVariables - ) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/convective_flux_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/convective_flux_reference.py deleted file mode 100644 index 3945805..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/convective_flux_reference.py +++ /dev/null @@ -1,42 +0,0 @@ -import numpy as np - -rho = 1.0 -Cp = 5.0 -u = 0.25 -v = 0.5 -w = 0.125 -p = 0.75 # lagrange pressure -T = u + v - -print("\nFlux with scaled density:\n") -fc_continuity = rho * np.array([u, v, w]) -print(fc_continuity) - -fc_momentum_0 = rho * np.array([u * u + p, u * v, u * w]) -print(fc_momentum_0) - -fc_momentum_1 = rho * np.array([u * v, v * v + p, v * w]) -print(fc_momentum_1) - -fc_momentum_2 = rho * np.array([u * w, v * w, w * w + p]) -print(fc_momentum_2) - -fc_energy = rho * Cp * np.array([u * T, v * T, w * T]) -print(fc_energy) - -print("\nFlux with unscaled density:\n") -rho = 3.0 -fc_continuity = rho * np.array([u, v, w]) -print(fc_continuity) - -fc_momentum_0 = np.array([rho * u * u + p, rho * u * v, rho * u * w]) -print(fc_momentum_0) - -fc_momentum_1 = np.array([rho * u * v, rho * v * v + p, rho * v * w]) -print(fc_momentum_1) - -fc_momentum_2 = np.array([rho * u * w, rho * v * w, rho * w * w + p]) -print(fc_momentum_2) - -fc_energy = rho * Cp * np.array([u * T, v * T, w * T]) -print(fc_energy) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/error_norms_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/error_norms_reference.py deleted file mode 100644 index 6684898..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/error_norms_reference.py +++ /dev/null @@ -1,17 +0,0 @@ -import math - -# computational/mms solution -comp = [0.1, 0.3, 0.4, 0.5, 0.8] -mms = [0.2, 0.7, 0.9, 1.1, 1.2] - -var_id = range(len(comp)) - -# L1 error norm -print("L1 norm (phi, vec{u}, T):") -for i in var_id: - print(abs(comp[i] - mms[i])) - -# L2 error norm -print("L2 norm (phi, vec{u}, T):") -for i in var_id: - print((comp[i] - mms[i])**2.0) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/liftdrag_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/liftdrag_reference.py deleted file mode 100644 index 0913083..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/liftdrag_reference.py +++ /dev/null @@ -1,67 +0,0 @@ -import numpy as np - -# NOTE: -# - The lift and drag calculations assume that the drag is -# in x-direction and lift is in y-direction. -# - Currently, lift/drag calculation ignores the turbulence model. - - -def twosym(t): - return t + t.T - - -def dev(t): - return t - (1 / t.shape[0]) * np.eye(t.shape[0]) * np.trace(t) - - -def lift_drag(rho, - nu, - lagrange_pressure, - normals, - grad_velocity, - dim, - is_compressible=False): - # Calculate shear stress with grad.U + transpose(grad.U) - tau = twosym(grad_velocity[0:dim, 0:dim]) - if is_compressible: tau = dev(tau) - viscous_force = -rho * nu * np.dot(tau, normals[0:dim]) - pressure_force = lagrange_pressure * normals[0:dim] - total_force = viscous_force + pressure_force - - return viscous_force, pressure_force, total_force - - -# Coefficients -# rho is given within the for loop for unscaled_density case -nu = 0.375 - -# Velocity vector, velocity and temperature gradients for viscous flux -velocity = np.array([0.25, 0.5, 0.125]) -grad_vel = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) -normals = np.array([-0.75, 1.5, -2.25]) -lagrange_pres = 0.75 - -# Create expected values for every mesh dimension and -# Scaled/Unscaled density cases -dims = [2, 3] -density_type = ['Scaled', 'Unscaled'] -calculation_types = ['Incompressible', 'Compressible'] -for cal_type in calculation_types: - print("\n" + cal_type + " case:") - for dim in dims: - print("\n" + str(dim) + "-D case:") - for den_type in density_type: - print("\n" + den_type + " Density:") - if den_type == 'Scaled': rho = 1.0 - if den_type == 'Unscaled': rho = 3.0 - - if cal_type == 'Incompressible': - viscous_force, pressure_force, total_force = lift_drag( - rho, nu, lagrange_pres, normals, grad_vel, dim) - if cal_type == 'Compressible': - viscous_force, pressure_force, total_force = lift_drag( - rho, nu, lagrange_pres, normals, grad_vel, dim, True) - print("Viscous Force:", viscous_force) - print("Pressure Force:", pressure_force) - print("Total Force:", total_force) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/local_time_step_size_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/local_time_step_size_reference.py deleted file mode 100644 index b5ceb8a..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/local_time_step_size_reference.py +++ /dev/null @@ -1,15 +0,0 @@ -# Dependent values -u = -1.5 -v = 2.0 -w = -0.5 -h0 = 0.25 -h1 = 0.5 -h2 = 0.75 - -# 2-D case -dt = 1.0 / (abs(u) / h0 + abs(v) / h1) -print("2-D dt: ", dt) - -# 3-D case -dt = 1.0 / (abs(u) / h0 + abs(v) / h1 + abs(w) / h2) -print("3-D dt: ", dt) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/planar_poiseuille_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/planar_poiseuille_reference.py deleted file mode 100644 index 28e0bbe..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/planar_poiseuille_reference.py +++ /dev/null @@ -1,35 +0,0 @@ -# Coefficients -nu = 50 -rho = 1.0 -k = 0.5 -cp = 100 -h_min = -4.0 -h_max = 4.0 -T_l = 305.0 -T_u = 300.0 -S_u = 24000 - -# Coordinates -x = 2.25 -y = 2.20 - -# Exact solution calculations -H = (h_max - h_min) / 2.0 -dT = T_l - T_u -U_avg = S_u * H * H / 3 / nu -Pr = cp * rho * nu / k -E = U_avg * U_avg / cp / dT - -## Velocity -u = 3.0 / 2.0 * U_avg * (1.0 - pow(y / H, 2.0)) -v = 0.0 - -print("x velocity: ", u) -print("y velocity: ", v) - -## Temperature -T_star = 0.5 * (1.0 - (y / H)) + 3.0 / 4.0 * Pr * E * (1.0 - pow(y / H, 4.0)) - -temp = T_star * dT + T_u - -print("Temperature: ", temp) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/rotating_annulus_exact_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/rotating_annulus_exact_reference.py deleted file mode 100644 index 5b2f09e..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/rotating_annulus_exact_reference.py +++ /dev/null @@ -1,40 +0,0 @@ -import numpy as np - -# Coefficients -nu = 50 -rho = 1.0 -k = 0.5 -Ro = 4.0 -Ri = 2.0 -omega = 0.5 -To = 274.0 -Ti = 273.0 - -# Coordinates -x = 2.25 -y = 2.20 - -# Exact solution calculations -r = np.sqrt(x * x + y * y) -xi = r / Ro -kappa = Ri / Ro - -## Velocity -u_phi = omega * Ro * Ro / (Ro * Ro - Ri * Ri) * (r - (Ri * Ri) / r) -u = -u_phi * y / r -v = u_phi * x / r - -print("x velocity: ", u) -print("y velocity: ", v) - -## Temperature -N = nu * rho * omega * omega * Ro * Ro / k / (To - Ti) * pow(kappa, 4.0) / pow( - 1.0 - pow(kappa, 2.0), 2.0) - -theta = (1.0 - np.log(xi) / np.log(kappa)) + N * ( - (1 - 1 / (xi * xi)) - (1 - 1 / - (kappa * kappa)) * np.log(xi) / np.log(kappa)) - -temp = theta * (To - Ti) + Ti - -print("Temperature: ", temp) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/shear_variables_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/shear_variables_reference.py deleted file mode 100644 index 4db438b..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/shear_variables_reference.py +++ /dev/null @@ -1,29 +0,0 @@ -import numpy as np - -# Coefficients -nu = 0.375 -rho = 1.5 - -# 2-D case -print("\n2-D case:") -grad_vel = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -normals = np.array([-0.125, 0.25]) - -tau_dot_n = grad_vel.dot(normals) -tau_dot_n = np.linalg.norm(tau_dot_n) - -print("Shear stress:", rho * nu * tau_dot_n) -print("Friction velocity:", np.sqrt(tau_dot_n * nu)) - -# 3-D case -print("\n3-D case:") -grad_vel = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.725, 1.45, -2.175]]) -normals = np.array([-0.125, 0.25, -0.375]) - -tau_dot_n = grad_vel.dot(normals) -tau_dot_n = np.linalg.norm(tau_dot_n) - -print("Shear stress:", rho * nu * tau_dot_n) -print("Friction velocity:", np.sqrt(tau_dot_n * nu), "\n") -strain_rate = 0.5 * (grad_vel + grad_vel.transpose()) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/taylor_green_vortex_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/taylor_green_vortex_reference.py deleted file mode 100644 index a3f505c..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/taylor_green_vortex_reference.py +++ /dev/null @@ -1,32 +0,0 @@ -import math - -# Node coordinates (set in the unit test) -x_list = [-0.15, -0.05, 0.05, 0.15] -y_list = [-0.05, 0.15, 0.35, 0.55] - - -# Function -def ic(x, y, time, nu): - Ft = math.exp(-2.0 * nu * time) - u = math.cos(x) * math.sin(y) * Ft - v = -math.sin(x) * math.cos(y) * Ft - p = -0.25 * (math.cos(2.0 * x) + math.cos(2.0 * y)) * Ft * Ft - return u, v, p - - -# Compute ic values -time = 0.5 -nu = 0.325 -p_list = [] -u_list = [] -v_list = [] -for x, y in zip(x_list, y_list): - u, v, p = ic(x, y, time, nu) - p_list.append(p) - u_list.append(u) - v_list.append(v) - -# Print ic values -print("phi: ", p_list) -print("u: ", u_list) -print("v: ", v_list) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/viscous_flux_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/viscous_flux_reference.py deleted file mode 100644 index a4ec4e6..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/viscous_flux_reference.py +++ /dev/null @@ -1,87 +0,0 @@ -import numpy as np - -# NOTE: -# - all momentum fluxes have to be multiplied by `nu` for -# laminar case and `nu + nu_t` for turbulent cases. -# - with unscaled density, all viscous fluxes are to be -# multiplied by rho -# - energy flux is multiplied by `k` for laminar case and -# `k + k_t` for turbulent cases. - - -def continuity_flux(rho, nu, nu_t, beta, grad_lagrange_pres, - build_turbulence_model, num_space_dim): - fv_continuity_flux = rho * nu * grad_lagrange_pres / beta - - return fv_continuity_flux[0:num_space_dim] - - -def momentum_flux(rho, nu, nu_t, grad_vel, build_turbulence_model, - num_space_dim): - fv_momentum_flux = rho * nu * grad_vel - if build_turbulence_model: fv_momentum_flux = rho * (nu + nu_t) * grad_vel - - return fv_momentum_flux[0:num_space_dim] - - -def energy_flux(kappa, kappa_t, grad_temp, build_turbulence_model, - num_space_dim): - fv_energy = kappa * grad_temp - if build_turbulence_model: fv_energy = (kappa + kappa_t) * grad_temp - - return fv_energy[0:num_space_dim] - - -# Coefficients -# rho and kappa_t is given within the for loop for unscaled_density case -kappa = 0.5 -nu = 0.375 -nu_t = 4.0 -cp = 0.2 -Pr_t = 0.8 -beta = 2.0 - -# Velocity vector, velocity and temperature gradients for viscous flux -velocity = np.array([0.25, 0.5, 0.125]) -grad_vel = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) -grad_temp = np.array([-0.75, 1.5, -2.25]) -grad_lagrange_pres = np.array([-0.75, 1.5, -2.25]) - -# Create expected values for every mesh dimension and -# Scaled/Unscaled density cases with/without turbulence model -dims = [2, 3] -density_type = ['Scaled', 'Unscaled'] -for dim in dims: - print("\n" + str(dim) + "-D case:") - for den_type in density_type: - print("\n" + den_type + " Density:") - for build_turbulence_model in [True, False]: - if den_type == 'Scaled': rho = 1.0 - if den_type == 'Unscaled': rho = 3.0 - kappa_t = rho * nu_t * cp / Pr_t - - print("\nTurbulence Model is " + str(build_turbulence_model) + - "\n") - - fv_continuity = continuity_flux(rho, nu, nu_t, beta, - grad_lagrange_pres, - build_turbulence_model, dim) - print("fv_continuity for AC Model:", np.zeros([dim])) - print("fv_continuity for EDAC Model:", fv_continuity) - - fv_momentum_0 = momentum_flux(rho, nu, nu_t, grad_vel[0], - build_turbulence_model, dim) - print("fv_momentum_0:", fv_momentum_0) - - fv_momentum_1 = momentum_flux(rho, nu, nu_t, grad_vel[1], - build_turbulence_model, dim) - print("fv_momentum_1:", fv_momentum_1) - - fv_momentum_2 = momentum_flux(rho, nu, nu_t, grad_vel[2], - build_turbulence_model, dim) - if dim > 2: print("fv_momentum_2:", fv_momentum_2) - - fv_energy = energy_flux(kappa, kappa_t, grad_temp, - build_turbulence_model, dim) - print("fv_energy:", fv_energy) diff --git a/src/incompressible_solver/closure_models/unit_test/doc/viscous_heat_reference.py b/src/incompressible_solver/closure_models/unit_test/doc/viscous_heat_reference.py deleted file mode 100644 index 3d93ce0..0000000 --- a/src/incompressible_solver/closure_models/unit_test/doc/viscous_heat_reference.py +++ /dev/null @@ -1,28 +0,0 @@ -import numpy as np - -# Coefficients -nu = 0.375 -rho = 1.0 - -# 2-D case -grad_vel = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -print("\n2-D case:") - -strain_rate_2D = 0.5 * (grad_vel + grad_vel.transpose()) - -fv_viscous_heat_2D = 2.0 * rho * nu * np.tensordot(strain_rate_2D, - strain_rate_2D) - -print("fv_viscous_heat:", fv_viscous_heat_2D) - -# 3-D case -grad_vel = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -strain_rate = 0.5 * (grad_vel + grad_vel.transpose()) - -print("\n3-D case:\n") - -fv_viscous_heat = 2.0 * rho * nu * np.tensordot(strain_rate, strain_rate) - -print("fv_viscous_heat:", fv_viscous_heat) diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleBuoyancySource.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleBuoyancySource.cpp deleted file mode 100644 index 4b391b6..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleBuoyancySource.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleBuoyancySource.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _T; - - PHX::MDField temperature; - - Dependencies(const panzer::IntegrationRule& ir, const double T) - : _T(T) - , temperature("temperature", ir.dl_scalar) - { - this->addEvaluatedField(temperature); - - this->setName("Incompressible Buoyancy Source Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "buoyancy source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = temperature.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - temperature(c, qp) = _T; - } - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Initialize class object to test - const double T = 330.0; - Teuchos::Array gravity(num_space_dim); - gravity[0] = 2.0; - gravity[1] = 3.0; - if (num_space_dim == 3) - gravity[2] = 4.0; - Teuchos::ParameterList user_params; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", true); - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 5.0); - fluid_prop_list.set("Build Buoyancy Source", true); - fluid_prop_list.set("Reference temperature", 300.0); - fluid_prop_list.set("Expansion coefficient", 1.0e-2); - user_params.set("Build Buoyancy Source", true); - user_params.set("Gravity", gravity); - - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - auto deps = Teuchos::rcp(new Dependencies(ir, T)); - test_fixture.registerEvaluator(deps); - - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleBuoyancySource( - ir, fluid_prop, user_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_buoyancy_continuity_source); - test_fixture.registerTestField(eval->_buoyancy_energy_source); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField( - eval->_buoyancy_momentum_source[dim]); - - test_fixture.evaluate(); - - const auto fc_cont = test_fixture.getTestFieldData( - eval->_buoyancy_continuity_source); - const auto fc_energy = test_fixture.getTestFieldData( - eval->_buoyancy_energy_source); - const auto fc_mom_0 = test_fixture.getTestFieldData( - eval->_buoyancy_momentum_source[0]); - const auto fc_mom_1 = test_fixture.getTestFieldData( - eval->_buoyancy_momentum_source[1]); - - const int num_point = ir.num_points; - - const double exp_mom_source[3] = {-0.6, -0.9, -1.2}; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_cont, 0, qp)); - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_energy, 0, qp)); - - EXPECT_DOUBLE_EQ(exp_mom_source[0], fieldValue(fc_mom_0, 0, qp)); - EXPECT_DOUBLE_EQ(exp_mom_source[1], fieldValue(fc_mom_1, 0, qp)); - if (num_space_dim > 2) // 3D mesh - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_buoyancy_momentum_source[2]); - EXPECT_DOUBLE_EQ(exp_mom_source[2], fieldValue(fc_mom_2, 0, qp)); - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleBuoyancySource2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleBuoyancySource2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleBuoyancySource3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleBuoyancySource3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - const Teuchos::Array gravity(num_space_dim); - test_fixture.user_params.set("Gravity", gravity); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleBuoyancySource"; - test_fixture.eval_name = "Incompressible Buoyancy Source " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleBuoyancySource, - num_space_dim>(); -} - -TEST(IncompressibleBuoyancySource_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleBuoyancySource_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleBuoyancySource_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleBuoyancySource_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConstantSource.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConstantSource.cpp deleted file mode 100644 index 6222088..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConstantSource.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConstantSource.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval(const bool build_temp_equ) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Initialize class object to test - Teuchos::Array mom_input_source(num_space_dim); - mom_input_source[0] = 0.1; - mom_input_source[1] = 0.2; - if (num_space_dim == 3) - mom_input_source[2] = 0.3; - Teuchos::ParameterList user_params; - user_params.set("Momentum Source", mom_input_source); - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - const double energy_input_source - = build_temp_equ ? 0.4 : std::numeric_limits::quiet_NaN(); - if (build_temp_equ) - { - user_params.set("Energy Source", energy_input_source); - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 5.0); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleConstantSource( - ir, fluid_prop, user_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_continuity_source); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_momentum_source[dim]); - - test_fixture.evaluate(); - - const auto fc_cont - = test_fixture.getTestFieldData(eval->_continuity_source); - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_momentum_source[0]); - const auto fc_mom_1 - = test_fixture.getTestFieldData(eval->_momentum_source[1]); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(0.0, fieldValue(fc_cont, 0, qp)); - EXPECT_EQ(mom_input_source[0], fieldValue(fc_mom_0, 0, qp)); - EXPECT_EQ(mom_input_source[1], fieldValue(fc_mom_1, 0, qp)); - if (num_space_dim > 2) // 3D mesh - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_momentum_source[2]); - EXPECT_EQ(mom_input_source[2], fieldValue(fc_mom_2, 0, qp)); - } - if (build_temp_equ) - { - const auto fc_energy = test_fixture.getTestFieldData( - eval->_energy_source); - EXPECT_EQ(energy_input_source, fieldValue(fc_energy, 0, qp)); - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSourceIsothermal2D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSourceIsothermal2D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSourceIsothermal3D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSourceIsothermal3D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSource3D, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConstantSource3D, jacobian_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - const Teuchos::Array mom_input_source(num_space_dim); - test_fixture.user_params.set("Momentum Source", mom_input_source); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleConstantSource"; - test_fixture.eval_name = "Incompressible Constant Source " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleConstantSource, - num_space_dim>(); -} - -TEST(IncompressibleConstantSource_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleConstantSource_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleConstantSource_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleConstantSource_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConvectiveFlux.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConvectiveFlux.cpp deleted file mode 100644 index 800293f..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleConvectiveFlux.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleConvectiveFlux.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u; - double _v; - double _w; - bool _build_temp; - - PHX::MDField lagrange_pressure; - PHX::MDField vel_0; - PHX::MDField vel_1; - PHX::MDField vel_2; - PHX::MDField temperature; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w, - const bool build_temp) - : _u(u) - , _v(v) - , _w(w) - , _build_temp(build_temp) - , lagrange_pressure("lagrange_pressure", ir.dl_scalar) - , vel_0("velocity_0", ir.dl_scalar) - , vel_1("velocity_1", ir.dl_scalar) - , vel_2("velocity_2", ir.dl_scalar) - , temperature("temperature", ir.dl_scalar) - - { - this->addEvaluatedField(lagrange_pressure); - this->addEvaluatedField(vel_0); - this->addEvaluatedField(vel_1); - this->addEvaluatedField(vel_2); - if (_build_temp) - this->addEvaluatedField(temperature); - - this->setName("Incompressible Convective Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - lagrange_pressure.deep_copy(0.75); - vel_0.deep_copy(_u); - vel_1.deep_copy(_v); - vel_2.deep_copy(_w); - if (_build_temp) - temperature.deep_copy(_u + _v); - } -}; - -template -void testEval(const bool unscaled_density, const bool build_temp) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Initialize velocity components and dependents - const double u = 0.25; - const double v = 0.5; - const double w - = num_space_dim > 2 ? 0.125 : std::numeric_limits::quiet_NaN(); - - auto deps - = Teuchos::rcp(new Dependencies(ir, u, v, w, build_temp)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - double rho = 1.0; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp); - if (unscaled_density) - { - rho = 3.0; - fluid_prop_list.set("Density", rho); - } - if (build_temp) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 5.0); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleConvectiveFlux( - ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_continuity_flux); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_momentum_flux[dim]); - - test_fixture.evaluate(); - - auto fc_cont - = test_fixture.getTestFieldData(eval->_continuity_flux); - auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_momentum_flux[0]); - - const int num_point = ir.num_points; - - // Expected values - const double exp_cont_flux[3] = {rho * u, rho * v, rho * w}; - const double exp_mom_0_flux[3] - = {unscaled_density ? 0.9375 : 0.8125, - unscaled_density ? 0.375 : 0.125, - num_space_dim == 3 ? unscaled_density ? 0.09375 : 0.03125 - : std::numeric_limits::quiet_NaN()}; - const double exp_mom_1_flux[3] - = {unscaled_density ? 0.375 : 0.125, - unscaled_density ? 1.5 : 1., - num_space_dim == 3 ? unscaled_density ? 0.1875 : 0.0625 - : std::numeric_limits::quiet_NaN()}; - const double exp_mom_2_flux[3] - = {unscaled_density ? 0.09375 : 0.03125, - unscaled_density ? 0.1875 : 0.0625, - num_space_dim == 3 ? unscaled_density ? 0.796875 : 0.765625 - : std::numeric_limits::quiet_NaN()}; - const double exp_ener_flux[3] - = {unscaled_density ? 2.8125 : 0.9375, - unscaled_density ? 5.625 : 1.875, - num_space_dim == 3 ? unscaled_density ? 1.40625 : 0.46875 - : std::numeric_limits::quiet_NaN()}; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_EQ(exp_cont_flux[dim], fieldValue(fc_cont, 0, qp, dim)); - EXPECT_EQ(exp_mom_0_flux[dim], fieldValue(fc_mom_0, 0, qp, dim)); - const auto fc_mom_1 = test_fixture.getTestFieldData( - eval->_momentum_flux[1]); - EXPECT_EQ(exp_mom_1_flux[dim], fieldValue(fc_mom_1, 0, qp, dim)); - if (num_space_dim > 2) // 3D mesh - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_momentum_flux[2]); - EXPECT_EQ(exp_mom_2_flux[dim], - fieldValue(fc_mom_2, 0, qp, dim)); - } - if (build_temp) - { - const auto fc_energy = test_fixture.getTestFieldData( - eval->_energy_flux); - EXPECT_EQ(exp_ener_flux[dim], - fieldValue(fc_energy, 0, qp, dim)); - } - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensityIsothermal2D, residual_test) -{ - testEval(false, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensityIsothermal2D, jacobian_test) -{ - testEval(false, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensity2D, residual_test) -{ - testEval(false, true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensity2D, jacobian_test) -{ - testEval(false, true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensityIsothermal3D, residual_test) -{ - testEval(false, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxScaledDensityIsothermal3D, jacobian_test) -{ - testEval(false, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxUnscaledDensityIsothermal3D, residual_test) -{ - testEval(true, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxUnscaledDensityIsothermal3D, jacobian_test) -{ - testEval(true, false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxUnscaledDensity3D, residual_test) -{ - testEval(true, true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleConvectiveFluxUnscaledDensity3D, jacobian_test) -{ - testEval(true, true); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleConvectiveFlux"; - test_fixture.eval_name = "Incompressible Convective Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleConvectiveFlux, - num_space_dim>(); -} - -TEST(IncompressibleConvectiveFlux_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleConvectiveFlux_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleConvectiveFlux_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleConvectiveFlux_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleErrorNorms.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleErrorNorms.cpp deleted file mode 100644 index afde8da..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleErrorNorms.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - // exact solution - PHX::MDField exact_phi; - Kokkos::Array, num_space_dim> - exact_velocity; - PHX::MDField exact_T; - - // numerical solution - PHX::MDField phi; - Kokkos::Array, - num_space_dim> - velocity; - PHX::MDField T; - - Dependencies(const panzer::IntegrationRule& ir) - : exact_phi("Exact_lagrange_pressure", ir.dl_scalar) - , exact_T("Exact_temperature", ir.dl_scalar) - , phi("lagrange_pressure", ir.dl_scalar) - , T("temperature", ir.dl_scalar) - { - // exact solution - this->addEvaluatedField(exact_phi); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, exact_velocity, "Exact_velocity_"); - this->addEvaluatedField(exact_T); - - // numerical solution - this->addEvaluatedField(phi); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, velocity, "velocity_"); - this->addEvaluatedField(T); - - this->setName("Incompressible Error Norms Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - // assign prescribed values to exact and numerical solution - exact_phi.deep_copy(0.1); - phi.deep_copy(0.2); - for (int i = 0; i < num_space_dim; ++i) - { - exact_velocity[i].deep_copy(0.3 + 0.1 * i); - velocity[i].deep_copy(0.7 + 0.2 * i); - } - exact_T.deep_copy(0.8); - T.deep_copy(1.2); - } -}; - -template -void testEval(const bool solve_temp) -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - const auto deps - = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList user_params; - user_params.set("Build Temperature Equation", solve_temp); - - // Create evaluator. - const auto eval = Teuchos::rcp( - new ClosureModel:: - IncompressibleErrorNorms( - ir, user_params)); - test_fixture.registerEvaluator(eval); - - // Add required test fields. - test_fixture.registerTestField(eval->_L1_error_continuity); - test_fixture.registerTestField(eval->_L2_error_continuity); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(eval->_L1_error_momentum[dim]); - test_fixture.registerTestField(eval->_L2_error_momentum[dim]); - } - if (solve_temp) - { - test_fixture.registerTestField(eval->_L1_error_energy); - test_fixture.registerTestField(eval->_L2_error_energy); - } - - // Evaluate - test_fixture.evaluate(); - - // Check the values - const auto L1_error_continuity - = test_fixture.getTestFieldData(eval->_L1_error_continuity); - const auto L1_error_momentum_0 - = test_fixture.getTestFieldData(eval->_L1_error_momentum[0]); - const auto L1_error_momentum_1 - = test_fixture.getTestFieldData(eval->_L1_error_momentum[1]); - const auto L2_error_continuity - = test_fixture.getTestFieldData(eval->_L2_error_continuity); - const auto L2_error_momentum_0 - = test_fixture.getTestFieldData(eval->_L2_error_momentum[0]); - const auto L2_error_momentum_1 - = test_fixture.getTestFieldData(eval->_L2_error_momentum[1]); - - // Reference values - const double L1_ref[5] = { - 0.1, 0.39999999999999997, 0.5, 0.6000000000000001, 0.3999999999999999}; - const double L2_ref[5] = {0.010000000000000002, - 0.15999999999999998, - 0.25, - 0.3600000000000001, - 0.15999999999999992}; - - // Check the L1/L2 error solutions - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(L1_ref[0], fieldValue(L1_error_continuity, 0, qp)); - EXPECT_DOUBLE_EQ(L1_ref[1], fieldValue(L1_error_momentum_0, 0, qp)); - EXPECT_DOUBLE_EQ(L1_ref[2], fieldValue(L1_error_momentum_1, 0, qp)); - - EXPECT_DOUBLE_EQ(L2_ref[0], fieldValue(L2_error_continuity, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[1], fieldValue(L2_error_momentum_0, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[2], fieldValue(L2_error_momentum_1, 0, qp)); - - if (solve_temp) - { - const auto L1_error_energy - = test_fixture.getTestFieldData( - eval->_L1_error_energy); - const auto L2_error_energy - = test_fixture.getTestFieldData( - eval->_L2_error_energy); - EXPECT_DOUBLE_EQ(L1_ref[num_space_dim + 1], - fieldValue(L1_error_energy, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[num_space_dim + 1], - fieldValue(L2_error_energy, 0, qp)); - } - - if (num_space_dim == 3) - { - const auto L1_error_momentum_2 - = test_fixture.getTestFieldData( - eval->_L1_error_momentum[2]); - const auto L2_error_momentum_2 - = test_fixture.getTestFieldData( - eval->_L2_error_momentum[2]); - - EXPECT_DOUBLE_EQ(L1_ref[num_space_dim], - fieldValue(L1_error_momentum_2, 0, qp)); - EXPECT_DOUBLE_EQ(L2_ref[num_space_dim], - fieldValue(L2_error_momentum_2, 0, qp)); - } - } -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2Isothermal_Error2D, residual_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2Isothermal_Error2D, jacobian_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2Isothermal_Error3D, residual_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2Isothermal_Error3D, jacobian_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2_Error3D, residual_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleL1L2_Error3D, jacobian_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressibleErrorNorm"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - if (num_space_dim == 2) - test_fixture.eval_name = "Incompressible Error Norms 2D"; - else if (num_space_dim == 3) - test_fixture.eval_name = "Incompressible Error Norms 3D"; - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.template buildAndTest< - ClosureModel::IncompressibleErrorNorms, - num_space_dim>(); -} - -TEST(IncompressibleErrorNorms_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleErrorNorms_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleErrorNorms_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleErrorNorms_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLiftDrag.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLiftDrag.cpp deleted file mode 100644 index cd17eb9..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLiftDrag.cpp +++ /dev/null @@ -1,372 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLiftDrag.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u; - double _v; - double _w; - bool _use_compressible_formula; - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField normals; - - PHX::MDField lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w, - const bool use_compressible_formula) - : _u(u) - , _v(v) - , _w(w) - , _use_compressible_formula(use_compressible_formula) - , grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , normals("Side Normal", ir.dl_vector) - , lagrange_pressure("lagrange_pressure", ir.dl_scalar) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - - this->addEvaluatedField(normals); - - this->addEvaluatedField(lagrange_pressure); - - this->setName("Incompressible Lift Drag Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "lift drag test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = _u * dimqp; - grad_vel_1(c, qp, dim) = _v * dimqp; - grad_vel_2(c, qp, dim) = _w * dimqp; - - normals(c, qp, dim) = (_u + _v) * dimqp; - } - - lagrange_pressure(c, qp) = (_u + _v); - } - } -}; - -template -void testEval(const bool unscaled_density, const bool use_compressible_formula) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - const double nan_val = std::numeric_limits::quiet_NaN(); - - // Initialize velocity components and dependents - const double u = 0.25; - const double v = 0.5; - const double w = num_space_dim == 3 - ? 0.125 - : std::numeric_limits::quiet_NaN(); - - auto deps = Teuchos::rcp( - new Dependencies(ir, u, v, w, use_compressible_formula)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - double rho = 1.0; - const double nu = 0.375; - - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", nu); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - if (unscaled_density) - { - rho = 3.0; - fluid_prop_list.set("Density", rho); - } - - Teuchos::ParameterList user_params; - user_params.set("Compressible Formula", use_compressible_formula); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel:: - IncompressibleLiftDrag( - ir, fluid_prop, user_params)); - test_fixture.registerEvaluator(eval); - - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField(eval->_total_force[dim]); - test_fixture.registerTestField(eval->_viscous_force[dim]); - test_fixture.registerTestField(eval->_pressure_force[dim]); - } - - test_fixture.evaluate(); - - const auto calc_total_force_0 - = test_fixture.getTestFieldData(eval->_total_force[0]); - const auto calc_viscous_force_0 - = test_fixture.getTestFieldData(eval->_viscous_force[0]); - const auto calc_pressure_force_0 - = test_fixture.getTestFieldData(eval->_pressure_force[0]); - const auto calc_total_force_1 - = test_fixture.getTestFieldData(eval->_total_force[1]); - const auto calc_viscous_force_1 - = test_fixture.getTestFieldData(eval->_viscous_force[1]); - const auto calc_pressure_force_1 - = test_fixture.getTestFieldData(eval->_pressure_force[1]); - - const int num_point = ir.num_points; - - // Expected values - const double exp_pressure_force_3d[3] = {-0.5625, 1.125, -1.6875}; - const double exp_pressure_force_2d[3] - = {exp_pressure_force_3d[0], exp_pressure_force_3d[1], nan_val}; - const double* exp_pressure_force - = num_space_dim == 3 ? exp_pressure_force_3d : exp_pressure_force_2d; - - const double exp_viscous_force_3d[3] - = {(unscaled_density ? -2.63671875 : -0.87890625), - (unscaled_density ? -6.5390625 : -2.1796875), - (unscaled_density ? -0.52734375 : -0.17578125)}; - const double exp_viscous_force_2d[3] - = {(unscaled_density ? -0.421875 : -0.140625), - (unscaled_density ? -3.375 : -1.125), - nan_val}; - const double exp_compressible_viscous_force_3d[3] - = {(unscaled_density ? -2.84765625 : -0.94921875), - (unscaled_density ? -6.1171875 : -2.0390625), - (unscaled_density ? -1.16015625 : -0.38671875)}; - const double exp_compressible_viscous_force_2d[3] - = {(unscaled_density ? -1.0546875 : -0.3515625), - (unscaled_density ? -2.109375 : -0.703125), - nan_val}; - const double* exp_viscous_force - = use_compressible_formula - ? (num_space_dim == 3 ? exp_compressible_viscous_force_3d - : exp_compressible_viscous_force_2d) - : (num_space_dim == 3 ? exp_viscous_force_3d - : exp_viscous_force_2d); - - const double exp_total_force_3d[3] - = {(unscaled_density ? -3.19921875 : -1.44140625), - (unscaled_density ? -5.4140625 : -1.0546875), - (unscaled_density ? -2.21484375 : -1.86328125)}; - const double exp_total_force_2d[3] - = {(unscaled_density ? -0.984375 : -0.703125), - (unscaled_density ? -2.25 : 0.), - nan_val}; - const double exp_compressible_total_force_3d[3] - = {(unscaled_density ? -3.41015625 : -1.51171875), - (unscaled_density ? -4.9921875 : -0.9140625), - (unscaled_density ? -2.84765625 : -2.07421875)}; - const double exp_compressible_total_force_2d[3] - = {(unscaled_density ? -1.6171875 : -0.9140625), - (unscaled_density ? -0.984375 : 0.421875), - nan_val}; - const double* exp_total_force - = use_compressible_formula - ? (num_space_dim == 3 ? exp_compressible_total_force_3d - : exp_compressible_total_force_2d) - : (num_space_dim == 3 ? exp_total_force_3d : exp_total_force_2d); - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - const auto calc_total_force - = test_fixture.getTestFieldData( - eval->_total_force[dim]); - const auto calc_viscous_force - = test_fixture.getTestFieldData( - eval->_viscous_force[dim]); - const auto calc_pressure_force - = test_fixture.getTestFieldData( - eval->_pressure_force[dim]); - - EXPECT_EQ(exp_total_force[dim], - fieldValue(calc_total_force, 0, qp)); - EXPECT_EQ(exp_viscous_force[dim], - fieldValue(calc_viscous_force, 0, qp)); - EXPECT_EQ(exp_pressure_force[dim], - fieldValue(calc_pressure_force, 0, qp)); - } - } -} - -//-----------------------------------------------------------------// -struct IncompressibleLiftDragTestParams -{ - std::string test_name; - bool unscaled_density; - bool use_compressible_formula; - int num_space_dim; -}; - -class IncompressibleLiftDragTest - : public testing::TestWithParam -{ - public: - struct PrintToStringParamName - { - template - std::string operator()(const testing::TestParamInfo& info) const - { - auto testParam - = static_cast(info.param); - return testParam.test_name; - } - }; -}; - -//-----------------------------------------------------------------// -TEST_P(IncompressibleLiftDragTest, cartesian) -{ - const auto params = GetParam(); - if (std::string::npos != params.test_name.find("residual")) - { - if (params.num_space_dim == 2) - { - testEval( - params.unscaled_density, params.use_compressible_formula); - } - else - { - testEval( - params.unscaled_density, params.use_compressible_formula); - } - } - else if (std::string::npos != params.test_name.find("jacobian")) - { - if (params.num_space_dim == 2) - { - testEval( - params.unscaled_density, params.use_compressible_formula); - } - else - { - testEval( - params.unscaled_density, params.use_compressible_formula); - } - } -} - -//-----------------------------------------------------------------// -INSTANTIATE_TEST_SUITE_P( - Test, - IncompressibleLiftDragTest, - testing::Values( - IncompressibleLiftDragTestParams{ - "ScaledDensity2D_residual", false, false, 2}, - IncompressibleLiftDragTestParams{ - "ScaledDensity2D_jacobian", false, false, 2}, - IncompressibleLiftDragTestParams{ - "UnScaledDensity2D_residual", true, false, 2}, - IncompressibleLiftDragTestParams{ - "UnScaledDensity2D_jacobian", true, false, 2}, - IncompressibleLiftDragTestParams{ - "ScaledDensity3D_residual", false, false, 3}, - IncompressibleLiftDragTestParams{ - "ScaledDensity3D_jacobian", false, false, 3}, - IncompressibleLiftDragTestParams{ - "UnScaledDensity3D_residual", true, false, 3}, - IncompressibleLiftDragTestParams{ - "UnScaledDensity3D_jacobian", true, false, 3}, - IncompressibleLiftDragTestParams{ - "ScaledDensityCompressible2D_residual", false, true, 2}, - IncompressibleLiftDragTestParams{ - "ScaledDensityCompressible2D_jacobian", false, true, 2}, - IncompressibleLiftDragTestParams{ - "UnScaledDensityCompressible2D_residual", true, true, 2}, - IncompressibleLiftDragTestParams{ - "UnScaledDensityCompressible2D_jacobian", true, true, 2}, - IncompressibleLiftDragTestParams{ - "ScaledDensityCompressible3D_residual", false, true, 3}, - IncompressibleLiftDragTestParams{ - "ScaledDensityCompressible3D_jacobian", false, true, 3}, - IncompressibleLiftDragTestParams{ - "UnScaledDensityCompressible3D_residual", true, true, 3}, - IncompressibleLiftDragTestParams{ - "UnScaledDensityCompressible3D_jacobian", true, true, 3}), - - IncompressibleLiftDragTest::PrintToStringParamName()); - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Compressible Formula", true); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleLiftDrag"; - test_fixture.eval_name = "Incompressible Lift/Drag " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleLiftDrag, - num_space_dim>(); -} - -TEST(IncompressibleLiftDrag_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleLiftDrag_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleLiftDrag_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleLiftDrag_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLocalTimeStepSize.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLocalTimeStepSize.cpp deleted file mode 100644 index 19acd5f..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleLocalTimeStepSize.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleLocalTimeStepSize.hpp" - -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u; - double _v; - double _w; - Kokkos::Array _h; - - PHX::MDField _element_length; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w, - const Kokkos::Array h) - : _u(u) - , _v(v) - , _w(w) - , _h(h) - , _element_length("element_length", ir.dl_vector) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - { - this->addEvaluatedField(_element_length); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - this->setName( - "Incompressible LocalTimeStepSize Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - int num_point = _element_length.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - _element_length(c, qp, 0) = _h[0]; - _element_length(c, qp, 1) = _h[1]; - _element_length(c, qp, 2) = _h[2]; - _velocity_0(c, qp) = _u; - _velocity_1(c, qp) = _v; - _velocity_2(c, qp) = _w; - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Eval dependencies. - const double u = -1.5; - const double v = 2.0; - const double w - = num_space_dim == 3 ? -0.5 : std::numeric_limits::quiet_NaN(); - const Kokkos::Array h = { - 0.25, - 0.5, - num_space_dim == 3 ? 0.75 : std::numeric_limits::quiet_NaN()}; - - auto dep_eval = Teuchos::rcp( - new Dependencies(*test_fixture.ir, u, v, w, h)); - test_fixture.registerEvaluator(dep_eval); - - // Create test evaluator. - auto dt_eval = Teuchos::rcp( - new ClosureModel::IncompressibleLocalTimeStepSize( - *test_fixture.ir)); - test_fixture.registerEvaluator(dt_eval); - - // Add required test fields. - test_fixture.registerTestField(dt_eval->_local_dt); - - // Evaluate test fields. - test_fixture.evaluate(); - - // Check the test fields. - auto local_dt_result - = test_fixture.getTestFieldData(dt_eval->_local_dt); - - const int num_qp = num_space_dim == 2 ? 4 : 8; - const double result = num_space_dim == 2 ? 0.1 : 0.09375; - for (int qp = 0; qp < num_qp; ++qp) - EXPECT_DOUBLE_EQ(result, fieldValue(local_dt_result, 0, qp)); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLocalTimeStepSize2D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLocalTimeStepSize2D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLocalTimeStepSize3D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLocalTimeStepSize3D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressibleLocalTimeStepSize"; - test_fixture.eval_name = "Incompressible Local Time Step Size"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::IncompressibleLocalTimeStepSize, - num_space_dim>(); -} - -TEST(IncompressibleLocalTimeStepSize_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleLocalTimeStepSize_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressiblePlanarPoiseuilleExact.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressiblePlanarPoiseuilleExact.cpp deleted file mode 100644 index 52db360..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressiblePlanarPoiseuilleExact.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressiblePlanarPoiseuilleExact.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - // Set up test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial quadrature point coordinates - auto ip_coord_view - = test_fixture.int_values->ip_coordinates.get_static_view(); - auto ip_coord_mirror = Kokkos::create_mirror(ip_coord_view); - ip_coord_mirror(0, 0, 0) = 2.25; - ip_coord_mirror(0, 0, 1) = 2.20; - Kokkos::deep_copy(ip_coord_view, ip_coord_mirror); - - // Get test fixture integrator rule - const auto& ir = *test_fixture.ir; - - // Set list of parameters to pass to the test evaluator - Teuchos::ParameterList user_params; - user_params.sublist("Planar Poiseuille Exact") - .set("H min", -4.0) - .set("H max", 4.0) - .set("Lower wall temperature", 305.0) - .set("Upper wall temperature", 300.0) - .set("Momentum source", 24000.0); - - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 50.0); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", true); - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 100.0); - - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create test evaluator - auto eval = Teuchos::rcp( - new ClosureModel::IncompressiblePlanarPoiseuilleExact( - ir, fluid_prop, user_params)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_velocity[dim]); - test_fixture.registerTestField(eval->_temperature); - - test_fixture.evaluate(); - - const auto fc_pressure - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto fc_mom_1 - = test_fixture.getTestFieldData(eval->_velocity[1]); - const auto fc_energy - = test_fixture.getTestFieldData(eval->_temperature); - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_pressure, 0, qp)); - EXPECT_DOUBLE_EQ(2678.4, fieldValue(fc_mom_0, 0, qp)); - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_mom_1, 0, qp)); - if (num_space_dim == 3) - { - const auto fc_mom_2 - = test_fixture.getTestFieldData(eval->_velocity[2]); - EXPECT_EQ(0.0, fieldValue(fc_mom_2, 0, qp)); - } - - EXPECT_EQ(446543149.12499994, fieldValue(fc_energy, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressiblePlanarPoiseuilleExact2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressiblePlanarPoiseuilleExact2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressiblePlanarPoiseuilleExact3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressiblePlanarPoiseuilleExact3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressiblePlanarPoiseuilleExact"; - test_fixture.eval_name = "Exact Solution Planar Poiseuille"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.user_params.sublist("Planar Poiseuille Exact") - .set("H min", -4.0) - .set("H max", 4.0) - .set("Lower wall temperature", 305.0) - .set("Upper wall temperature", 300.0) - .set("Momentum source", 24000.0); - test_fixture.template buildAndTest< - ClosureModel::IncompressiblePlanarPoiseuilleExact, - num_space_dim>(); -} - -TEST(IncompressiblePlanarPoiseuilleExact_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressiblePlanarPoiseuilleExact_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressiblePlanarPoiseuilleExact_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressiblePlanarPoiseuilleExact_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleRotatingAnnulusExact.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleRotatingAnnulusExact.cpp deleted file mode 100644 index a65145a..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleRotatingAnnulusExact.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleRotatingAnnulusExact.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - // Set up test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 0; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial quadrature point coordinates - auto ip_coord_view - = test_fixture.int_values->ip_coordinates.get_static_view(); - auto ip_coord_mirror = Kokkos::create_mirror(ip_coord_view); - ip_coord_mirror(0, 0, 0) = 2.25; - ip_coord_mirror(0, 0, 1) = 2.20; - Kokkos::deep_copy(ip_coord_view, ip_coord_mirror); - - // Get test fixture integrator rule - auto& ir = *test_fixture.ir; - - // Set list of parameters to pass to the test evaluator - Teuchos::ParameterList user_params; - user_params.set("Outer radius", 4.0); - user_params.set("Inner radius", 2.0); - user_params.set("Angular velocity", 0.5); - user_params.set("Outer wall temperature", 274.0); - user_params.set("Inner wall temperature", 273.0); - - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 50.0); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", true); - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 5.0); - - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create test evaluator - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleRotatingAnnulusExact( - ir, fluid_prop, user_params)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_velocity[dim]); - test_fixture.registerTestField(eval->_temperature); - - test_fixture.evaluate(); - - const auto fc_pressure - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto fc_mom_1 - = test_fixture.getTestFieldData(eval->_velocity[1]); - const auto fc_energy - = test_fixture.getTestFieldData(eval->_temperature); - - const int num_point = ir.num_points; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(0.0, fieldValue(fc_pressure, 0, qp)); - EXPECT_DOUBLE_EQ(-0.8742236808886645, fieldValue(fc_mom_0, 0, qp)); - EXPECT_DOUBLE_EQ(0.8940924009088613, fieldValue(fc_mom_1, 0, qp)); - if (num_space_dim == 3) - { - const auto fc_mom_2 - = test_fixture.getTestFieldData(eval->_velocity[2]); - EXPECT_EQ(0.0, fieldValue(fc_mom_2, 0, qp)); - } - - EXPECT_EQ(292.43421676378887, fieldValue(fc_energy, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRotatingAnnulusExact2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRotatingAnnulusExact2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRotatingAnnulusExact3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRotatingAnnulusExact3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressibleRotatingAnnulusExact"; - test_fixture.eval_name = "Exact Solution Rotating Annulus"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.user_params.set("Outer radius", 2.0) - .set("Inner radius", 3.0) - .set("Angular velocity", 4.0) - .set("Outer wall temperature", 5.0) - .set("Inner wall temperature", 6.0); - test_fixture.template buildAndTest< - ClosureModel::IncompressibleRotatingAnnulusExact, - num_space_dim>(); -} - -TEST(IncompressibleRotatingAnnulusExact_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleRotatingAnnulusExact_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleRotatingAnnulusExact_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleRotatingAnnulusExact_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleShearVariables.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleShearVariables.cpp deleted file mode 100644 index 23c2881..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleShearVariables.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleShearVariables.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField normals; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(normals); - - this->setName("Incompressible Shear Variables Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "shear variables test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.725 * dimqp - : _nanval; - normals(c, qp, dim) = 0.125 * dimqp; - } - } - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize velocity gradient and dependents - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Density", 1.5); - fluid_prop_list.set("Artificial compressibility", 1.0); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleShearVariables( - ir, fluid_prop)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_tau_w); - test_fixture.registerTestField(eval->_u_tau); - - test_fixture.evaluate(); - - const auto tau_w = test_fixture.getTestFieldData(eval->_tau_w); - const auto u_tau = test_fixture.getTestFieldData(eval->_u_tau); - - const int num_point = ir.num_points; - - // Expected values - const double exp_tau_w = num_space_dim == 3 ? 0.9011871138178397 - : 0.19652941208494246; - const double exp_u_tau = num_space_dim == 3 ? 0.7751073533465498 - : 0.3619663079025841; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_tau_w, fieldValue(tau_w, 0, qp)); - EXPECT_DOUBLE_EQ(exp_u_tau, fieldValue(u_tau, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleShearVariables2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleShearVariables2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleShearVariables3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleShearVariables3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressibleShearVariables"; - test_fixture.eval_name = "Incompressible Shear/Friction Variables " - + std::to_string(num_space_dim) + "D"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::IncompressibleShearVariables, - num_space_dim>(); -} - -TEST(IncompressibleShearVariables_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleShearVariables_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleShearVariables_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleShearVariables_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTaylorGreenVortexExactSolution.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTaylorGreenVortexExactSolution.cpp deleted file mode 100644 index 7db0b18..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTaylorGreenVortexExactSolution.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTaylorGreenVortexExactSolution.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - constexpr int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - const auto& ir = *test_fixture.ir; - const double time = 0.5; - test_fixture.setTime(time); - - // Set non-trivial coordinates for the degree of freedom - const int num_point = ir.num_points; - auto ip_coord_view - = test_fixture.int_values->ip_coordinates.get_static_view(); - auto ip_coord_mirror = Kokkos::create_mirror(ip_coord_view); - for (int dim = 0; dim < num_space_dim; dim++) - { - for (int qp = 0; qp < num_point; ++qp) - { - ip_coord_mirror(0, qp, dim) = 0.1 * (dim + 1) * (qp + 1) - 0.25; - } - } - Kokkos::deep_copy(ip_coord_view, ip_coord_mirror); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.325); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - const auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleTaylorGreenVortexExactSolution< - EvalType, - panzer::Traits, - num_space_dim>(ir, fluid_prop)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_velocity[dim]); - test_fixture.evaluate(); - - const auto exact_cont - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto exact_mom_0 - = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto exact_mom_1 - = test_fixture.getTestFieldData(eval->_velocity[1]); - - // Reference values - const double phi_ref[4] = {-0.2545417754691831, - -0.2545417754691831, - -0.2296800890258847, - -0.18388182976977963}; - const double u_ref[4] = {-0.035705825747158935, - 0.10783820008203808, - 0.2474434185973639, - 0.37341515252860846}; - const double v_ref[4] = {0.10783820008203808, - 0.035705825747158935, - -0.03392198573058794, - -0.09204974820065662}; - - // Assert values - const double tol = 1.0e-16; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_NEAR(phi_ref[qp], fieldValue(exact_cont, 0, qp), tol); - EXPECT_NEAR(u_ref[qp], fieldValue(exact_mom_0, 0, qp), tol); - EXPECT_NEAR(v_ref[qp], fieldValue(exact_mom_1, 0, qp), tol); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleTaylorGreenVortexExactSolution, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleTaylorGreenVortexExactSolution, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.type_name = "IncompressibleTaylorGreenVortexExactSolution"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.eval_name - = "Incompressible Taylor Green Vortex Exact Solution"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleTaylorGreenVortexExactSolution, - num_space_dim>(); -} - -TEST(IncompressibleTaylorGreenVortexExactSolution_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleTaylorGreenVortexExactSolution_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTimeDerivative.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTimeDerivative.cpp deleted file mode 100644 index e1daf25..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleTimeDerivative.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleTimeDerivative.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -#include -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _dphi_dt; - double _du_dt; - double _dv_dt; - double _dw_dt; - bool _build_temp_equ; - - PHX::MDField _dvdt_lagrange_pressure; - PHX::MDField _dvdt_velocity_0; - PHX::MDField _dvdt_velocity_1; - PHX::MDField _dvdt_velocity_2; - PHX::MDField _dvdt_temperature; - - Dependencies(const panzer::IntegrationRule& ir, - const double dphi_dt, - const double du_dt, - const double dv_dt, - const double dw_dt, - const bool build_temp_equ) - : _dphi_dt(dphi_dt) - , _du_dt(du_dt) - , _dv_dt(dv_dt) - , _dw_dt(dw_dt) - , _build_temp_equ(build_temp_equ) - , _dvdt_lagrange_pressure("DXDT_lagrange_pressure", ir.dl_scalar) - , _dvdt_velocity_0("DXDT_velocity_0", ir.dl_scalar) - , _dvdt_velocity_1("DXDT_velocity_1", ir.dl_scalar) - , _dvdt_velocity_2("DXDT_velocity_2", ir.dl_scalar) - , _dvdt_temperature("DXDT_temperature", ir.dl_scalar) - { - this->addEvaluatedField(_dvdt_lagrange_pressure); - this->addEvaluatedField(_dvdt_velocity_0); - this->addEvaluatedField(_dvdt_velocity_1); - this->addEvaluatedField(_dvdt_velocity_2); - if (_build_temp_equ) - this->addEvaluatedField(_dvdt_temperature); - this->setName("Incompressible Time Derivative Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - _dvdt_lagrange_pressure.deep_copy(_dphi_dt); - _dvdt_velocity_0.deep_copy(_du_dt); - _dvdt_velocity_1.deep_copy(_dv_dt); - _dvdt_velocity_2.deep_copy(_dw_dt); - if (_build_temp_equ) - _dvdt_temperature.deep_copy(_du_dt + _dv_dt); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool unscaled_density, const bool build_temp_equ) -{ - // Setup test fixture. - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Eval dependencies. - double dphi_dt = 0.125; - double du_dt = 1.25; - double dv_dt = 1.5; - double dw_dt - = num_space_dim == 3 ? 1.75 : std::numeric_limits::quiet_NaN(); - auto dep_eval = Teuchos::rcp(new Dependencies( - *test_fixture.ir, dphi_dt, du_dt, dv_dt, dw_dt, build_temp_equ)); - test_fixture.registerEvaluator(dep_eval); - - // Fluid properties - const double beta = 0.1; - double rho = 1.0; - const double Cp - = build_temp_equ ? 5.0 : std::numeric_limits::quiet_NaN(); - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", beta); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (unscaled_density) - { - rho = 2.0; - fluid_prop_list.set("Density", rho); - } - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", Cp); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create test evaluator. - auto dqdt_eval = Teuchos::rcp( - new ClosureModel::IncompressibleTimeDerivative( - *test_fixture.ir, fluid_prop)); - test_fixture.registerEvaluator(dqdt_eval); - - // Add required test fields. - test_fixture.registerTestField(dqdt_eval->_dqdt_continuity); - for (int dim = 0; dim < num_space_dim; dim++) - { - test_fixture.registerTestField( - dqdt_eval->_dqdt_momentum[dim]); - } - - // Evaluate test fields. - test_fixture.evaluate(); - - // Expected momentum values - const double exp_mom[3] = {rho * du_dt, rho * dv_dt, rho * dw_dt}; - - // Expected energy values - const double exp_ener = rho * Cp * (du_dt + dv_dt); - - // Check the test fields. - auto continuity_result - = test_fixture.getTestFieldData(dqdt_eval->_dqdt_continuity); - - EXPECT_DOUBLE_EQ(0.125 / beta, fieldValue(continuity_result, 0, 0)); - for (int dim = 0; dim < num_space_dim; dim++) - { - auto momentum_dim_result = test_fixture.getTestFieldData( - dqdt_eval->_dqdt_momentum[dim]); - EXPECT_DOUBLE_EQ(exp_mom[dim], fieldValue(momentum_dim_result, 0, 0)); - } - if (build_temp_equ) - { - const auto energy_result - = test_fixture.getTestFieldData(dqdt_eval->_dqdt_energy); - EXPECT_DOUBLE_EQ(exp_ener, fieldValue(energy_result, 0, 0)); - } -} // namespace Test - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensityIsothermal2D, residual_test) -{ - testEval(false, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensityIsothermal2D, jacobian_test) -{ - testEval(false, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensity2D, residual_test) -{ - testEval(false, true); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensity2D, jacobian_test) -{ - testEval(false, true); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensityIsothermal3D, residual_test) -{ - testEval(false, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeScaledDensityIsothermal3D, jacobian_test) -{ - testEval(false, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeUnscaledDensityIsothermal3D, residual_test) -{ - testEval(true, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeUnscaledDensityIsothermal3D, jacobian_test) -{ - testEval(true, false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeUnscaledDensity3D, residual_test) -{ - testEval(true, true); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivativeUnscaledDensity3D, jacobian_test) -{ - testEval(true, true); -} - -//---------------------------------------------------------------------------// - -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleTimeDerivative"; - test_fixture.eval_name = "Incompressible Time Derivative " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleTimeDerivative, - num_space_dim>(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivative_Factory2D, residual_test) -{ - testFactory(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivative_Factory2D, jacobian_test) -{ - testFactory(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivative_Factory3D, residual_test) -{ - testFactory(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTimeDerivative_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleVariableTimeDerivative.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleVariableTimeDerivative.cpp deleted file mode 100644 index 63472d8..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleVariableTimeDerivative.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _dxdt_sav; - - Dependencies(const panzer::IntegrationRule& ir) - : _dxdt_sav("DXDT_spalart_allmaras_variable", ir.dl_scalar) - { - this->addEvaluatedField(_dxdt_sav); - this->setName( - "IncompressibleVariableTimeDerivative Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - _dxdt_sav.deep_copy(2.0); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Setup test fixture. - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Eval dependencies. - auto dep_eval = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Closure parameters - Teuchos::ParameterList closure_params; - closure_params.set("Field Name", "spalart_allmaras_variable"); - closure_params.set("Equation Name", "spalart_allmaras_equation"); - - // Create test evaluator. - auto dqdt_eval = Teuchos::rcp( - new ClosureModel::IncompressibleVariableTimeDerivative( - *test_fixture.ir, closure_params)); - test_fixture.registerEvaluator(dqdt_eval); - - // Add required test fields. - test_fixture.registerTestField(dqdt_eval->_dqdt_var_eq); - - // Evaluate test fields. - test_fixture.evaluate(); - - // Check the test fields. - const auto sae_result - = test_fixture.getTestFieldData(dqdt_eval->_dqdt_var_eq); - EXPECT_DOUBLE_EQ(2.0, fieldValue(sae_result, 0, 0)); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleVariableTimeDerivative, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleVariableTimeDerivative, jacobian_test) -{ - testEval(); -} - -} // namespace Test -} // end namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousFlux.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousFlux.cpp deleted file mode 100644 index 68e187a..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousFlux.cpp +++ /dev/null @@ -1,602 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousFlux.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Continuity Equation cases -enum class ContinuityModel -{ - AC, - EDAC -}; -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - double _u; - double _v; - double _w; - bool _build_temp_equ; - bool _build_turbulence_model; - ContinuityModel _continuity_model; - - PHX::MDField velocity_0; - PHX::MDField velocity_1; - PHX::MDField velocity_2; - PHX::MDField nu_t; - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField - grad_temperature; - - PHX::MDField - grad_lagrange_pressure; - - Dependencies(const panzer::IntegrationRule& ir, - const double u, - const double v, - const double w, - const bool build_temp_equ, - const bool build_turbulence_model, - const ContinuityModel continuity_model) - : _u(u) - , _v(v) - , _w(w) - , _build_temp_equ(build_temp_equ) - , _build_turbulence_model(build_turbulence_model) - , _continuity_model(continuity_model) - , velocity_0("velocity_0", ir.dl_scalar) - , velocity_1("velocity_1", ir.dl_scalar) - , velocity_2("velocity_2", ir.dl_scalar) - , nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , grad_temperature("GRAD_temperature", ir.dl_vector) - , grad_lagrange_pressure("GRAD_lagrange_pressure", ir.dl_vector) - { - this->addEvaluatedField(velocity_0); - this->addEvaluatedField(velocity_1); - this->addEvaluatedField(velocity_2); - - if (_build_turbulence_model) - { - this->addEvaluatedField(nu_t); - } - - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - - if (_build_temp_equ) - this->addEvaluatedField(grad_temperature); - - this->addEvaluatedField(grad_lagrange_pressure); - - this->setName("Incompressible Viscous Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "viscous flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = _u * dimqp; - grad_vel_1(c, qp, dim) = _v * dimqp; - grad_vel_2(c, qp, dim) = _w * dimqp; - if (_build_temp_equ) - grad_temperature(c, qp, dim) = (_u + _v) * dimqp; - - grad_lagrange_pressure(c, qp, dim) - = _continuity_model == ContinuityModel::EDAC - ? (_u + _v) * dimqp - : _nanval; - } - - velocity_0(c, qp) = _u; - velocity_1(c, qp) = _v; - velocity_2(c, qp) = _w; - - if (_build_turbulence_model) - { - nu_t(c, qp) = 4.0; - } - } - } -}; - -template -void testEval(const bool unscaled_density, - const bool build_temp_equ, - const bool build_turbulence_model, - const ContinuityModel continuity_model) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - const double nan_val = std::numeric_limits::quiet_NaN(); - - // Initialize velocity components and dependents - const double u = 0.25; - const double v = 0.5; - const double w = num_space_dim == 3 ? 0.125 : nan_val; - const double Pr_t = 0.8; - - const auto deps = Teuchos::rcp(new Dependencies( - ir, u, v, w, build_temp_equ, build_turbulence_model, continuity_model)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - double rho = 1.0; - const double nu = 0.375; - const double cp = 0.2; - const double beta = 2.0; - const double kappa = build_temp_equ ? 0.5 : nan_val; - - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", nu); - fluid_prop_list.set("Artificial compressibility", beta); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (unscaled_density) - { - rho = 3.0; - fluid_prop_list.set("Density", rho); - } - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", kappa); - fluid_prop_list.set("Specific heat capacity", cp); - } - - Teuchos::ParameterList user_params; - if (build_turbulence_model && build_temp_equ) - user_params.set("Turbulent Prandtl Number", Pr_t); - if (continuity_model == ContinuityModel::EDAC) - user_params.set("Continuity Model", "EDAC"); - - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel:: - IncompressibleViscousFlux( - ir, fluid_prop, user_params, build_turbulence_model)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_continuity_flux); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_momentum_flux[dim]); - - test_fixture.evaluate(); - - const auto fc_cont - = test_fixture.getTestFieldData(eval->_continuity_flux); - const auto fc_mom_0 - = test_fixture.getTestFieldData(eval->_momentum_flux[0]); - - const int num_point = ir.num_points; - - const double exp_cont_flux_3d_ac[3] = {0.0, 0.0, 0.0}; - const double exp_cont_flux_3d_edac[3] - = {unscaled_density ? -0.421875 : -0.140625, - unscaled_density ? 0.84375 : 0.28125, - unscaled_density ? -1.265625 : -0.421875}; - - const double exp_cont_flux_2d_ac[3] - = {exp_cont_flux_3d_ac[0], exp_cont_flux_3d_ac[1], nan_val}; - - const double exp_cont_flux_2d_edac[3] - = {exp_cont_flux_3d_edac[0], exp_cont_flux_3d_edac[1], nan_val}; - - const double* exp_cont_flux - = continuity_model == ContinuityModel::EDAC - ? (num_space_dim == 3 ? exp_cont_flux_3d_edac - : exp_cont_flux_2d_edac) - : (num_space_dim == 3 ? exp_cont_flux_3d_ac - : exp_cont_flux_2d_ac); - - const double exp_mom_0_flux_3d[3] - = {build_turbulence_model ? (unscaled_density ? -3.28125 : -1.09375) - : (unscaled_density ? -0.28125 : -0.09375), - build_turbulence_model ? (unscaled_density ? 6.5625 : 2.1875) - : (unscaled_density ? 0.5625 : 0.1875), - build_turbulence_model ? (unscaled_density ? -9.84375 : -3.28125) - : (unscaled_density ? -0.84375 : -0.28125)}; - const double exp_mom_0_flux_2d[3] - = {exp_mom_0_flux_3d[0], exp_mom_0_flux_3d[1], nan_val}; - const double* exp_mom_0_flux = num_space_dim == 3 ? exp_mom_0_flux_3d - : exp_mom_0_flux_2d; - const double exp_mom_1_flux_3d[3] - = {build_turbulence_model ? (unscaled_density ? -6.5625 : -2.1875) - : (unscaled_density ? -0.5625 : -0.1875), - build_turbulence_model ? (unscaled_density ? 13.125 : 4.375) - : (unscaled_density ? 1.125 : 0.375), - build_turbulence_model ? (unscaled_density ? -19.6875 : -6.5625) - : (unscaled_density ? -1.6875 : -0.5625)}; - const double exp_mom_1_flux_2d[3] - = {exp_mom_1_flux_3d[0], exp_mom_1_flux_3d[1], nan_val}; - const double* exp_mom_1_flux = num_space_dim == 3 ? exp_mom_1_flux_3d - : exp_mom_1_flux_2d; - const double exp_mom_2_flux_3d[3] = { - build_turbulence_model ? (unscaled_density ? -1.640625 : -0.546875) - : (unscaled_density ? -0.140625 : -0.046875), - build_turbulence_model ? (unscaled_density ? 3.28125 : 1.09375) - : (unscaled_density ? 0.28125 : 0.09375), - build_turbulence_model ? (unscaled_density ? -4.921875 : -1.640625) - : (unscaled_density ? -0.421875 : -0.140625)}; - const double exp_mom_2_flux_2d[3] = {nan_val, nan_val, nan_val}; - const double* exp_mom_2_flux = num_space_dim == 3 ? exp_mom_2_flux_3d - : exp_mom_2_flux_2d; - - const double exp_ene_flux_3d[3] = { - build_turbulence_model ? (unscaled_density ? -2.625 : -1.125) : -0.375, - build_turbulence_model ? (unscaled_density ? 5.25 : 2.25) : 0.75, - build_turbulence_model ? (unscaled_density ? -7.875 : -3.375) : -1.125}; - - const double exp_ene_flux_2d[3] - = {exp_ene_flux_3d[0], exp_ene_flux_3d[1], nan_val}; - const double* exp_ene_flux = num_space_dim == 3 ? exp_ene_flux_3d - : exp_ene_flux_2d; - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_EQ(exp_cont_flux[dim], fieldValue(fc_cont, 0, qp, dim)); - EXPECT_EQ(exp_mom_0_flux[dim], fieldValue(fc_mom_0, 0, qp, dim)); - const auto fc_mom_1 = test_fixture.getTestFieldData( - eval->_momentum_flux[1]); - EXPECT_EQ(exp_mom_1_flux[dim], fieldValue(fc_mom_1, 0, qp, dim)); - if (num_space_dim > 2) // 3D mesh - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_momentum_flux[2]); - EXPECT_EQ(exp_mom_2_flux[dim], - fieldValue(fc_mom_2, 0, qp, dim)); - } - if (build_temp_equ) - { - const auto fc_ene = test_fixture.getTestFieldData( - eval->_energy_flux); - EXPECT_DOUBLE_EQ(exp_ene_flux[dim], - fieldValue(fc_ene, 0, qp, dim)); - } - } - } -} - -//-----------------------------------------------------------------// -struct IncompressibleViscousFluxTestParams -{ - std::string test_name; - bool unscaled_density; - bool build_temp_equ; - bool build_turbulence_model; - ContinuityModel continuity_model; - int num_space_dim; -}; - -class IncompressibleViscousFluxTest - : public testing::TestWithParam -{ - public: - struct PrintToStringParamName - { - template - std::string operator()(const testing::TestParamInfo& info) const - { - auto testParam - = static_cast(info.param); - return testParam.test_name; - } - }; -}; - -//-----------------------------------------------------------------// -TEST_P(IncompressibleViscousFluxTest, cartesian) -{ - const auto params = GetParam(); - if (std::string::npos != params.test_name.find("residual")) - { - if (params.num_space_dim == 2) - { - testEval(params.unscaled_density, - params.build_temp_equ, - params.build_turbulence_model, - params.continuity_model); - } - else - { - testEval(params.unscaled_density, - params.build_temp_equ, - params.build_turbulence_model, - params.continuity_model); - } - } - else if (std::string::npos != params.test_name.find("jacobian")) - { - if (params.num_space_dim == 2) - { - testEval(params.unscaled_density, - params.build_temp_equ, - params.build_turbulence_model, - params.continuity_model); - } - else - { - testEval(params.unscaled_density, - params.build_temp_equ, - params.build_turbulence_model, - params.continuity_model); - } - } -} - -//-----------------------------------------------------------------// -INSTANTIATE_TEST_SUITE_P( - Test, - IncompressibleViscousFluxTest, - testing::Values( - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermal2D_" - "residual", - false, - false, - false, - ContinuityModel::AC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermal2D_" - "jacobian", - false, - false, - false, - ContinuityModel::AC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensity2D_residual", - false, - true, - false, - ContinuityModel::AC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensity2D_jacobian", - false, - true, - false, - ContinuityModel::AC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermal3D_" - "residual", - false, - false, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermal3D_" - "jacobian", - false, - false, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"UnScaledDensityIsothermal3D_" - "residual", - true, - false, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"UnScaledDensityIsothermal3D_" - "jacobian", - true, - false, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensity3D_residual", - false, - true, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensity3D_jacobian", - false, - true, - false, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentScaledDensity3D_" - "residual", - false, - true, - true, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentScaledDensity3D_" - "jacobian", - false, - true, - true, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentUnScaledDensity3D_" - "residual", - true, - true, - true, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentUnScaledDensity3D_" - "jacobian", - true, - true, - true, - ContinuityModel::AC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermalEDAC2D_" - "residual", - false, - false, - false, - ContinuityModel::EDAC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermalEDAC2D_" - "jacobian", - false, - false, - false, - ContinuityModel::EDAC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityEDAC2D_residual", - false, - true, - false, - ContinuityModel::EDAC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityEDAC2D_jacobian", - false, - true, - false, - ContinuityModel::EDAC, - 2}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermalEDAC3D_" - "residual", - false, - false, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensityIsothermalEDAC3D_" - "jacobian", - false, - false, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"UnScaledDensityIsothermalEDAC3D_" - "residual", - true, - false, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"UnScaledDensityIsothermalEDAC3D_" - "jacobian", - true, - false, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensityEDAC3D_residual", - false, - true, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"ScaledDensityEDAC3D_jacobian", - false, - true, - false, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentScaledDensityEDAC3D_" - "residual", - false, - true, - true, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentScaledDensityEDAC3D_" - "jacobian", - false, - true, - true, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentUnScaledDensityEDAC3D_" - "residual", - true, - true, - true, - ContinuityModel::EDAC, - 3}, - IncompressibleViscousFluxTestParams{"TurbulentUnScaledDensityEDAC3D_" - "jacobian", - true, - true, - true, - ContinuityModel::EDAC, - 3}), - IncompressibleViscousFluxTest::PrintToStringParamName()); - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.type_name = "IncompressibleViscousFlux"; - test_fixture.eval_name = "Incompressible Viscous Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::IncompressibleViscousFlux, - num_space_dim>(); -} - -TEST(IncompressibleViscousFlux_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousFlux_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousFlux_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousFlux_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousHeat.cpp b/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousHeat.cpp deleted file mode 100644 index c716777..0000000 --- a/src/incompressible_solver/closure_models/unit_test/tstIncompressibleViscousHeat.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include -#include - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleViscousHeat.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - - this->setName("Incompressible Viscous Heat Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "viscous heat test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - } - } - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize velocity gradient and dependents - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Build Temperature Equation", true); - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Density", 1.0); - fluid_prop_list.set("Artificial compressibility", 1.0); - fluid_prop_list.set("Thermal conductivity", 1.0); - fluid_prop_list.set("Specific heat capacity", 1.0); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - auto eval = Teuchos::rcp( - new ClosureModel:: - IncompressibleViscousHeat( - ir, fluid_prop)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField( - eval->_viscous_heat_continuity_source); - test_fixture.registerTestField(eval->_viscous_heat_energy_source); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField( - eval->_viscous_heat_momentum_source[dim]); - - test_fixture.evaluate(); - - const auto fc_cont = test_fixture.getTestFieldData( - eval->_viscous_heat_continuity_source); - const auto fc_mom_0 = test_fixture.getTestFieldData( - eval->_viscous_heat_momentum_source[0]); - const auto fc_mom_1 = test_fixture.getTestFieldData( - eval->_viscous_heat_momentum_source[1]); - const auto fc_energy = test_fixture.getTestFieldData( - eval->_viscous_heat_energy_source); - - const int num_point = ir.num_points; - - // Expected energy - see ./doc/viscous_heat_reference.py - const double exp_energy = num_space_dim == 3 ? 1.775390625 : 0.796875; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(0.0, fieldValue(fc_cont, 0, qp)); - EXPECT_EQ(0.0, fieldValue(fc_mom_0, 0, qp)); - EXPECT_EQ(0.0, fieldValue(fc_mom_1, 0, qp)); - if (num_space_dim == 3) - { - const auto fc_mom_2 = test_fixture.getTestFieldData( - eval->_viscous_heat_momentum_source[2]); - EXPECT_EQ(0.0, fieldValue(fc_mom_2, 0, qp)); - } - EXPECT_EQ(exp_energy, fieldValue(fc_energy, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleViscousHeat2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleViscousHeat2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleViscousHeat3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleViscousHeat3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.type_name = "IncompressibleViscousHeat"; - test_fixture.eval_name = "Incompressible Viscous Heat " - + std::to_string(num_space_dim) + "D"; - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0); - test_fixture.template buildAndTest< - ClosureModel::IncompressibleViscousHeat, - num_space_dim>(); -} - -TEST(IncompressibleViscousHeat_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousHeat_Factory2D, jacobian_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousHeat_Factory3D, residual_test) -{ - testFactory(); -} - -TEST(IncompressibleViscousHeat_Factory3D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp b/src/incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp deleted file mode 100644 index 1ea3f94..0000000 --- a/src/incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef VERTEXCFD_CONSTANTFLUIDPROPERTIES_HPP -#define VERTEXCFD_CONSTANTFLUIDPROPERTIES_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace FluidProperties -{ -//---------------------------------------------------------------------------// -// Constant fluid properties -//---------------------------------------------------------------------------// - -class ConstantFluidProperties -{ - public: - ConstantFluidProperties() = default; - explicit ConstantFluidProperties(const Teuchos::ParameterList& params) - : _kinematic_viscosity(params.get("Kinematic viscosity")) - , _beta(params.get("Artificial compressibility")) - , _solve_temp(params.get("Build Temperature Equation")) - , _build_ind_less_equ(false) - , _build_buoyancy(false) - { - // Density - if (params.isType("Density")) - { - _density = params.get("Density"); - } - else - { - _density = 1.0; - } - - // Thermal parameters - if (_solve_temp) - { - _thermal_conductivity = params.get("Thermal conductivity"); - _Cp = params.get("Specific heat capacity"); - - // Check for buoyancy bool - if (params.isType("Build Buoyancy Source")) - { - _build_buoyancy = params.get("Build Buoyancy Source"); - } - } - else - { - _thermal_conductivity = std::numeric_limits::quiet_NaN(); - _Cp = std::numeric_limits::quiet_NaN(); - } - - // Buoyancy source term - - if (_build_buoyancy) - { - _beta_T = params.get("Expansion coefficient"); - _T_ref = params.get("Reference temperature"); - } - else - { - _beta_T = std::numeric_limits::quiet_NaN(); - _T_ref = std::numeric_limits::quiet_NaN(); - } - - // Inductionless MHD equation - if (params.isType("Build Inductionless MHD Equation")) - { - _build_ind_less_equ - = params.get("Build Inductionless MHD Equation"); - } - - if (_build_ind_less_equ) - { - _sigma = params.get("Electrical conductivity"); - } - else - { - _sigma = std::numeric_limits::quiet_NaN(); - } - } - - // Constant density - KOKKOS_INLINE_FUNCTION double constantDensity() const { return _density; } - - // Constant kinematic viscosity - KOKKOS_INLINE_FUNCTION double constantKinematicViscosity() const - { - return _kinematic_viscosity; - } - - // Constant thermal conductivity - KOKKOS_INLINE_FUNCTION double constantThermalConductivity() const - { - return _thermal_conductivity; - } - - // Constant heat capacity - KOKKOS_INLINE_FUNCTION double constantHeatCapacity() const - { - return _density * _Cp; - } - - // Constant electrical conductivity - KOKKOS_INLINE_FUNCTION double constantElectricalConductivity() const - { - return _sigma; - } - - // Solve temperature equation - KOKKOS_INLINE_FUNCTION bool solveTemperature() const - { - return _solve_temp; - } - - // Include buoyancy effects - KOKKOS_INLINE_FUNCTION bool buildBuoyancy() const - { - return _build_buoyancy; - } - - // Expansion coefficient - KOKKOS_INLINE_FUNCTION double expansionCoefficient() const - { - return _beta_T; - } - - // Reference temperature - KOKKOS_INLINE_FUNCTION double referenceTemperature() const - { - return _T_ref; - } - - // Artificial compressibility - KOKKOS_INLINE_FUNCTION double artificialCompressibility() const - { - return _beta; - } - - private: - double _kinematic_viscosity; - double _beta; - double _density; - double _Cp; - double _thermal_conductivity; - double _sigma; - double _beta_T; - double _T_ref; - bool _solve_temp; - bool _build_ind_less_equ; - bool _build_buoyancy; -}; - -} // namespace FluidProperties -} // namespace VertexCFD - -#endif // VERTEXCFD_CONSTANTFLUIDPROPERTIES_HPP diff --git a/src/incompressible_solver/fluid_properties/unit_test/CMakeLists.txt b/src/incompressible_solver/fluid_properties/unit_test/CMakeLists.txt deleted file mode 100644 index 0d91d21..0000000 --- a/src/incompressible_solver/fluid_properties/unit_test/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - IncompressibleConstantFluidProperties - ) diff --git a/src/incompressible_solver/fluid_properties/unit_test/tstIncompressibleConstantFluidProperties.cpp b/src/incompressible_solver/fluid_properties/unit_test/tstIncompressibleConstantFluidProperties.cpp deleted file mode 100644 index 92a77ef..0000000 --- a/src/incompressible_solver/fluid_properties/unit_test/tstIncompressibleConstantFluidProperties.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include - -#include - -using namespace VertexCFD::FluidProperties; - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace Test -{ -class IncompressibleConstantFluidPropertiesTest : public ::testing::Test -{ - protected: - std::unique_ptr cfp; - - virtual void SetUp() override; - virtual void SetUpDensity(); - virtual void SetUpTemperature(); - virtual void SetUpBuoyancy(); - virtual void SetUpElectricPotential(); -}; - -// Set up all variables but density -void IncompressibleConstantFluidPropertiesTest::SetUp() -{ - Teuchos::ParameterList cfp_params; - cfp_params.set("Kinematic viscosity", 0.2); - cfp_params.set("Artificial compressibility", 2.0); - cfp_params.set("Build Temperature Equation", false); - cfp = std::make_unique(cfp_params); -} - -// Initialize density -void IncompressibleConstantFluidPropertiesTest::SetUpDensity() -{ - Teuchos::ParameterList cfp_params; - cfp_params.set("Density", 1.5); - cfp_params.set("Kinematic viscosity", 0.2); - cfp_params.set("Artificial compressibility", 2.0); - cfp_params.set("Build Temperature Equation", false); - cfp = std::make_unique(cfp_params); -} - -// With temperature equation -void IncompressibleConstantFluidPropertiesTest::SetUpTemperature() -{ - Teuchos::ParameterList cfp_params; - cfp_params.set("Kinematic viscosity", 0.2); - cfp_params.set("Artificial compressibility", 2.0); - cfp_params.set("Build Temperature Equation", true); - cfp_params.set("Thermal conductivity", 0.3); - cfp_params.set("Specific heat capacity", 0.4); - cfp = std::make_unique(cfp_params); -} - -// With buoyancy -void IncompressibleConstantFluidPropertiesTest::SetUpBuoyancy() -{ - Teuchos::ParameterList cfp_params; - cfp_params.set("Kinematic viscosity", 0.2); - cfp_params.set("Artificial compressibility", 2.0); - cfp_params.set("Build Temperature Equation", true); - cfp_params.set("Build Buoyancy Source", true); - cfp_params.set("Thermal conductivity", 0.3); - cfp_params.set("Specific heat capacity", 0.4); - cfp_params.set("Expansion coefficient", 0.5); - cfp_params.set("Reference temperature", 0.6); - cfp = std::make_unique(cfp_params); -} - -// With electric potential equation -void IncompressibleConstantFluidPropertiesTest::SetUpElectricPotential() -{ - Teuchos::ParameterList cfp_params; - cfp_params.set("Kinematic viscosity", 0.2); - cfp_params.set("Artificial compressibility", 2.0); - cfp_params.set("Build Temperature Equation", false); - cfp_params.set("Build Inductionless MHD Equation", true); - cfp_params.set("Electrical conductivity", 0.3); - cfp = std::make_unique(cfp_params); -} - -// Density - specified -TEST_F(IncompressibleConstantFluidPropertiesTest, unscaled_density) -{ - IncompressibleConstantFluidPropertiesTest::SetUpDensity(); - const double rho_expect = 1.5; - const double rho = cfp->constantDensity(); - EXPECT_DOUBLE_EQ(rho_expect, rho); - - const double nu_expect = 0.2; - const double nu = cfp->constantKinematicViscosity(); - EXPECT_DOUBLE_EQ(nu_expect, nu); - - const double beta_expect = 2.0; - const double beta = cfp->artificialCompressibility(); - EXPECT_DOUBLE_EQ(beta_expect, beta); -} - -// Density - not specified -TEST_F(IncompressibleConstantFluidPropertiesTest, scaled_density) -{ - IncompressibleConstantFluidPropertiesTest::SetUp(); - const double rho_expect = 1.0; - const double rho = cfp->constantDensity(); - EXPECT_DOUBLE_EQ(rho_expect, rho); - - const double nu_expect = 0.2; - const double nu = cfp->constantKinematicViscosity(); - EXPECT_DOUBLE_EQ(nu_expect, nu); - - const double beta_expect = 2.0; - const double beta = cfp->artificialCompressibility(); - EXPECT_DOUBLE_EQ(beta_expect, beta); -} - -// Temperature equation -TEST_F(IncompressibleConstantFluidPropertiesTest, temperature_equation) -{ - IncompressibleConstantFluidPropertiesTest::SetUpTemperature(); - const double k_expect = 0.3; - const double k = cfp->constantThermalConductivity(); - EXPECT_DOUBLE_EQ(k_expect, k); - - const double cp_expect = 0.4; - const double cp = cfp->constantHeatCapacity(); - EXPECT_DOUBLE_EQ(cp_expect, cp); -} - -// Buoyancy -TEST_F(IncompressibleConstantFluidPropertiesTest, bouyancy) -{ - IncompressibleConstantFluidPropertiesTest::SetUpBuoyancy(); - const double beta_expect = 0.5; - const double beta = cfp->expansionCoefficient(); - EXPECT_DOUBLE_EQ(beta_expect, beta); - - const double T0_expect = 0.6; - const double T0 = cfp->referenceTemperature(); - EXPECT_DOUBLE_EQ(T0_expect, T0); -} - -// Inductionless mhd equation -TEST_F(IncompressibleConstantFluidPropertiesTest, inductionless_mhd_equation) -{ - IncompressibleConstantFluidPropertiesTest::SetUpElectricPotential(); - const double sigma_expect = 0.3; - const double sigma = cfp->constantElectricalConductivity(); - EXPECT_DOUBLE_EQ(sigma_expect, sigma); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.cpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.cpp deleted file mode 100644 index 4522e3e..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp" -#include "VertexCFD_InitialCondition_IncompressibleLaminarFlow_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::IncompressibleLaminarFlow) diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp deleted file mode 100644 index 8e53712..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleLaminarFlow - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleLaminarFlow( - const Teuchos::ParameterList& ic_params, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField _temperature; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; - - bool _solve_temp; - double _min; - double _max; - double _vel_avg; - double _vel_max; - double _T_init; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_HPP diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow_impl.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow_impl.hpp deleted file mode 100644 index c972895..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow_impl.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP - -#include - -#include -#include -#include -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleLaminarFlow::IncompressibleLaminarFlow( - const Teuchos::ParameterList& ic_params, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const panzer::PureBasis& basis) - : _lagrange_pressure("lagrange_pressure", basis.functional) - , _temperature("temperature", basis.functional) - , _basis_name(basis.name()) - , _solve_temp(fluid_prop.solveTemperature()) - , _min(ic_params.get("Minimum height")) - , _max(ic_params.get("Maximum height")) - , _vel_avg(ic_params.get("Average velocity")) - , _vel_max(num_space_dim == 2 ? 3.0 / 2.0 * _vel_avg : 2.0 * _vel_avg) - , _T_init(std::numeric_limits::quiet_NaN()) -{ - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - - Utils::addEvaluatedVectorField( - *this, basis.functional, _velocity, "velocity_", true); - - if (_solve_temp) - { - _T_init = ic_params.get("Temperature"); - this->addEvaluatedField(_temperature); - this->addUnsharedField(_temperature.fieldTag().clone()); - } - - this->setName("Laminar Flow Initial Condition"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleLaminarFlow::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _velocity[0].extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - double r2 = _basis_coords(cell, basis, 1) - * _basis_coords(cell, basis, 1); - const double H = 0.5 * (_max - _min); - _velocity[1](cell, basis) = 0.0; - if (num_space_dim == 3) - { - r2 += _basis_coords(cell, basis, 2) - * _basis_coords(cell, basis, 2); - _velocity[2](cell, basis) = 0.0; - } - _velocity[0](cell, basis) = _vel_max * (1.0 - r2 / (H * H)); - _lagrange_pressure(cell, basis) = 0.0; - if (_solve_temp) - _temperature(cell, basis) = _T_init; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLELAMINARFLOW_IMPL_HPP diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.cpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.cpp deleted file mode 100644 index 5d2133f..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp" -#include "VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::IncompressibleTaylorGreenVortex) diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp deleted file mode 100644 index f98962f..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleTaylorGreenVortex - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleTaylorGreenVortex(const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_HPP diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex_impl.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex_impl.hpp deleted file mode 100644 index b29d133..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex_impl.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_IMPL_HPP - -#include - -#include -#include -#include -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleTaylorGreenVortex:: - IncompressibleTaylorGreenVortex(const panzer::PureBasis& basis) - : _lagrange_pressure("lagrange_pressure", basis.functional) - , _basis_name(basis.name()) -{ - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - - Utils::addEvaluatedVectorField( - *this, basis.functional, _velocity, "velocity_", true); - - this->setName("Taylor-Green Vortex Initial Condition"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortex:: - postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortex::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleTaylorGreenVortex::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _lagrange_pressure.extent(1); - - using std::cos; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - const double x = _basis_coords(cell, basis, 0); - const double y = _basis_coords(cell, basis, 1); - _lagrange_pressure(cell, basis) = -0.25 - * (cos(2.0 * x) + cos(2.0 * y)); - _velocity[0](cell, basis) = cos(x) * sin(y); - _velocity[1](cell, basis) = -sin(x) * cos(y); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLETAYLORGREENVORTEX_IMPL_HPP diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.cpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.cpp deleted file mode 100644 index 52c3c23..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp" -#include "VertexCFD_InitialCondition_IncompressibleVortexInBox_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::InitialCondition::IncompressibleVortexInBox) diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp deleted file mode 100644 index 7c956a5..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class IncompressibleVortexInBox - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - - IncompressibleVortexInBox(const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _lagrange_pressure; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_HPP diff --git a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox_impl.hpp b/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox_impl.hpp deleted file mode 100644 index fd39cda..0000000 --- a/src/incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox_impl.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_IMPL_HPP - -#include -#include -#include -#include - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -IncompressibleVortexInBox::IncompressibleVortexInBox( - const panzer::PureBasis& basis) - : _velocity_0("velocity_0", basis.functional) - , _velocity_1("velocity_1", basis.functional) - , _lagrange_pressure("lagrange_pressure", basis.functional) - , _basis_name(basis.name()) -{ - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_velocity_0.fieldTag().clone()); - this->addUnsharedField(_velocity_1.fieldTag().clone()); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - this->setName("Vertex In the Box Initial Condition"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVortexInBox::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVortexInBox::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVortexInBox::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _velocity_0.extent(1); - - using Constants::pi; - using std::cos; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - const double x = _basis_coords(cell, basis, 0); - const double y = _basis_coords(cell, basis, 1); - _velocity_0(cell, basis) = -2.0 * cos(pi * y) * sin(pi * y) - * sin(pi * x) * sin(pi * x); - _velocity_1(cell, basis) = 2.0 * cos(pi * x) * sin(pi * x) - * sin(pi * y) * sin(pi * y); - _lagrange_pressure(cell, basis) = 0.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INCOMPRESSIBLEVORTEXINBOX_IMPL_HPP diff --git a/src/incompressible_solver/initial_conditions/unit_test/CMakeLists.txt b/src/incompressible_solver/initial_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index e482df5..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - IncompressibleVortexInBox - IncompressibleLaminarFlow - IncompressibleTaylorGreenVortex - ) diff --git a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_laminar_flow_reference.py b/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_laminar_flow_reference.py deleted file mode 100644 index b1fbb0f..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_laminar_flow_reference.py +++ /dev/null @@ -1,56 +0,0 @@ -# Functions -def ic2d(y, h_min, h_max, vel_avg): - coeff = 3.0 / 2.0 - H = 0.5 * (h_max - h_min) - u = vel_avg * coeff * (1.0 - y * y / (H * H)) - return u, 0.0 - - -def ic3d(y, z, h_min, h_max, vel_avg): - coeff = 2.0 - H = 0.5 * (h_max - h_min) - r2 = y * y + z * z - u = vel_avg * coeff * (1.0 - r2 / (H * H)) - return u, 0.0, 0.0 - - -# IC parameters -h_min = -2.0 -h_max = 2.2 -vel_avg = 3.0 - -# Compute ic values in 2D -num_points = 4 -y_list = [(basis + 1) * 0.125 for basis in range(0, num_points)] -u_list = [] -v_list = [] -for y in y_list: - u, v = ic2d(y, h_max, h_min, vel_avg) - u_list.append(u) - v_list.append(v) - -# Print ic values -print("\n2D laminar flow:") -print("phi: ", 0.0) -print("u: ", u_list) -print("v: ", v_list) - -# Compute ic values in 3D -num_points = 8 -y_list = [(basis + 1) * 0.125 for basis in range(0, num_points)] -z_list = [h_min + y for y in y_list] -u_list = [] -v_list = [] -w_list = [] -for y, z in zip(y_list, z_list): - u, v, w = ic3d(y, z, h_min, h_max, vel_avg) - u_list.append(u) - v_list.append(v) - w_list.append(w) - -# Print ic values -print("\n3D laminar flow:") -print("phi: ", 0.0) -print("u: ", u_list) -print("v: ", v_list) -print("w: ", w_list) diff --git a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_taylor_green_vortex_reference.py b/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_taylor_green_vortex_reference.py deleted file mode 100644 index 207421b..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_taylor_green_vortex_reference.py +++ /dev/null @@ -1,29 +0,0 @@ -import math - -# Node coordinates (set in the unit test) -x_list = [-0.15, -0.05, 0.05, 0.15] -y_list = [-0.05, 0.15, 0.35, 0.55] - - -# Function -def ic(x, y): - u = math.cos(x) * math.sin(y) - v = -math.sin(x) * math.cos(y) - p = -0.25 * (math.cos(2.0 * x) + math.cos(2.0 * y)) - return u, v, p - - -# Compute ic values -u_list = [] -v_list = [] -p_list = [] -for x, y in zip(x_list, y_list): - u, v, p = ic(x, y) - p_list.append(p) - u_list.append(u) - v_list.append(v) - -# Print ic values -print("phi: ", p_list) -print("u: ", u_list) -print("v: ", v_list) diff --git a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_vortex_box_reference.py b/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_vortex_box_reference.py deleted file mode 100644 index a480333..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/doc/incompressible_vortex_box_reference.py +++ /dev/null @@ -1,30 +0,0 @@ -import math - -pi = math.pi - -# Node coordinates (set in the unit test) -x_list = [-0.15, -0.05, 0.05, 0.15] -y_list = [-0.05, 0.15, 0.35, 0.55] - - -# Function -def ic(x, y): - u = -2.0 * math.cos(pi * y) * math.sin(pi * y) * math.sin( - pi * x) * math.sin(pi * x) - v = 2.0 * math.cos(pi * x) * math.sin(pi * x) * math.sin( - pi * y) * math.sin(pi * y) - return u, v - - -# Compute ic values -u_list = [] -v_list = [] -for x, y in zip(x_list, y_list): - u, v = ic(x, y) - u_list.append(u) - v_list.append(v) - -# Print ic values -print("phi: ", 0.0) -print("u: ", u_list) -print("v: ", v_list) diff --git a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleLaminarFlow.cpp b/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleLaminarFlow.cpp deleted file mode 100644 index fd64e1c..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleLaminarFlow.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include - -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval(const bool build_temp_equ) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial coordinates for the degree of freedom - const int num_coord = test_fixture.cell_topo->getNodeCount(); - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - for (int basis = 0; basis < num_coord; basis++) - { - // random coordinate assigned - basis_coord_mirror(0, basis, 1) = 0.125 * (basis + 1); - if (num_space_dim == 3) - basis_coord_mirror(0, basis, 2) = -2.0 + 0.125 * (basis + 1); - } - - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - Teuchos::ParameterList ic_params; - ic_params.set("Minimum height", -2.0); - ic_params.set("Maximum height", 2.2); - ic_params.set("Average velocity", 3.0); - const double temp_ref - = build_temp_equ ? 4.0 : std::numeric_limits::quiet_NaN(); - if (build_temp_equ) - ic_params.set("Temperature", temp_ref); - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - if (build_temp_equ) - { - fluid_prop_list.set("Thermal conductivity", 0.5); - fluid_prop_list.set("Specific heat capacity", 0.6); - } - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Register and evaluate fields - auto eval = Teuchos::rcp( - new InitialCondition::IncompressibleLaminarFlow( - ic_params, fluid_prop, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - - test_fixture.registerTestField(eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_velocity[dim]); - if (build_temp_equ) - test_fixture.registerTestField(eval->_temperature); - - test_fixture.evaluate(); - - const auto phi - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto u = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto v = test_fixture.getTestFieldData(eval->_velocity[1]); - - // Check number of degree of freedoms - const int num_dofs = num_space_dim == 2 ? 4 : 8; - EXPECT_EQ(num_dofs, phi.extent(1)); - EXPECT_EQ(num_dofs, u.extent(1)); - EXPECT_EQ(num_dofs, v.extent(1)); - - // Reference values - const double phi_ref = 0.0; - const double u_ref_2d[4] = {4.48405612244898, - 4.436224489795919, - 4.356505102040816, - 4.244897959183674}; - const double u_ref_3d[8] = {1.195578231292517, - 1.7482993197278913, - 2.2159863945578238, - 2.5986394557823127, - 2.896258503401361, - 3.1088435374149666, - 3.2363945578231297, - 3.278911564625851}; - const double v_ref = 0.0; - const double w_ref = 0.0; - - // Check values - for (int n = 0; n < num_dofs; ++n) - { - EXPECT_DOUBLE_EQ(phi_ref, fieldValue(phi, 0, n)); - const double u_ref = num_space_dim == 2 ? u_ref_2d[n] : u_ref_3d[n]; - EXPECT_FLOAT_EQ(u_ref, fieldValue(u, 0, n)); - EXPECT_FLOAT_EQ(v_ref, fieldValue(v, 0, n)); - if (num_space_dim == 3) - { - const auto w - = test_fixture.getTestFieldData(eval->_velocity[2]); - EXPECT_EQ(num_dofs, w.extent(1)); - EXPECT_FLOAT_EQ(w_ref, fieldValue(w, 0, n)); - } - if (build_temp_equ) - { - const auto temp - = test_fixture.getTestFieldData(eval->_temperature); - EXPECT_EQ(num_dofs, temp.extent(1)); - EXPECT_FLOAT_EQ(temp_ref, fieldValue(temp, 0, n)); - } - } -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLowIsothermal2D, residual_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLowIsothermal2D, jacobian_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLowIsothermal3D, residual_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLowIsothermal3D, jacobian_test) -{ - testEval(false); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLow3D, residual_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleLaminarFLow3D, jacobian_test) -{ - testEval(true); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleTaylorGreenVortex.cpp b/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleTaylorGreenVortex.cpp deleted file mode 100644 index c1f9707..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleTaylorGreenVortex.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include - -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial coordinates for the degree of freedom - const int num_coord = test_fixture.cell_topo->getNodeCount(); - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - for (int dim = 0; dim < num_space_dim; dim++) - { - for (int basis = 0; basis < num_coord; basis++) - { - // random coordinate assigned - x(dim, basis) = 0.1 * (dim + 1) * (basis + 1) - 0.25; - basis_coord_mirror(0, basis, dim) = x(dim, basis); - } - } - - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - // Register and evaluate fields - const auto eval = Teuchos::rcp( - new InitialCondition:: - IncompressibleTaylorGreenVortex( - *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_velocity[dim]); - - test_fixture.evaluate(); - - const auto phi - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto u = test_fixture.getTestFieldData(eval->_velocity[0]); - const auto v = test_fixture.getTestFieldData(eval->_velocity[1]); - - // Check number of degree of freedoms - const int num_dofs = 4; - EXPECT_EQ(num_dofs, phi.extent(1)); - EXPECT_EQ(num_dofs, u.extent(1)); - EXPECT_EQ(num_dofs, v.extent(1)); - - // Reference values - const double phi_ref[4] = {-0.487585163600908, - -0.487585163600908, - -0.4399615881406286, - -0.3522331526377958}; - const double u_ref[4] = {-0.04941795707411653, - 0.14925137372094469, - 0.34246927448499503, - 0.5168180147731708}; - const double v_ref[4] = {0.14925137372094469, - 0.04941795707411653, - -0.04694906782365546, - -0.12739967246452027}; - - // Check values - for (int n = 0; n < num_dofs; ++n) - { - EXPECT_DOUBLE_EQ(phi_ref[n], fieldValue(phi, 0, n)); - EXPECT_FLOAT_EQ(u_ref[n], fieldValue(u, 0, n)); - EXPECT_FLOAT_EQ(v_ref[n], fieldValue(v, 0, n)); - } -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTaylorGreenVortex2D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleTaylorGreenVortex2D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleVortexInBox.cpp b/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleVortexInBox.cpp deleted file mode 100644 index bf71c22..0000000 --- a/src/incompressible_solver/initial_conditions/unit_test/tstIncompressibleVortexInBox.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include - -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set non-trivial coordinates for the degree of freedom - const int num_coord = test_fixture.cell_topo->getNodeCount(); - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - for (int dim = 0; dim < num_space_dim; dim++) - { - for (int basis = 0; basis < num_coord; basis++) - { - // random coordinate assigned - x(dim, basis) = 0.1 * (dim + 1) * (basis + 1) - 0.25; - basis_coord_mirror(0, basis, dim) = x(dim, basis); - } - } - - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - // Register and evaluate fields - auto eval = Teuchos::rcp( - new InitialCondition::IncompressibleVortexInBox( - *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_lagrange_pressure); - test_fixture.registerTestField(eval->_velocity_0); - test_fixture.registerTestField(eval->_velocity_1); - - test_fixture.evaluate(); - - const auto phi - = test_fixture.getTestFieldData(eval->_lagrange_pressure); - const auto u = test_fixture.getTestFieldData(eval->_velocity_0); - const auto v = test_fixture.getTestFieldData(eval->_velocity_1); - - // Check number of degree of freedoms - const int num_dofs = 4; - EXPECT_EQ(num_dofs, phi.extent(1)); - EXPECT_EQ(num_dofs, u.extent(1)); - EXPECT_EQ(num_dofs, v.extent(1)); - - // Reference values - const double phi_ref = 0.0; - const double u_ref[4] = {0.0636906811868036, - -0.019798055040567034, - -0.019798055040567034, - 0.06369068118680365}; - const double v_ref[4] = {-0.019798055040567034, - -0.0636906811868036, - 0.2453263131881438, - 0.7892189393343801}; - - // Check values - for (int n = 0; n < num_dofs; ++n) - { - EXPECT_DOUBLE_EQ(phi_ref, fieldValue(phi, 0, n)); - EXPECT_FLOAT_EQ(u_ref[n], fieldValue(u, 0, n)); - EXPECT_FLOAT_EQ(v_ref[n], fieldValue(v, 0, n)); - } -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleVortexInBox, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(IncompressibleVortexInBox, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.cpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.cpp deleted file mode 100644 index 566710e..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_ElectricPotentialFixed.hpp" -#include "VertexCFD_BoundaryState_ElectricPotentialFixed_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::ElectricPotentialFixed) diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp deleted file mode 100644 index 4cbd443..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_HPP -#define VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class ElectricPotentialFixed : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ElectricPotentialFixed(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_electric_potential; - - PHX::MDField - _boundary_grad_electric_potential; - - private: - int _num_grad_dim; - double _time; - double _time_init; - double _time_final; - double _a_sc; - double _b_sc; - - PHX::MDField - _grad_electric_potential; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_HPP diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed_impl.hpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed_impl.hpp deleted file mode 100644 index 72e8cbe..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed_impl.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This function should be used for Dirichlet boundary conditions. A ramping -// in time can be enabled. -//---------------------------------------------------------------------------// -template -ElectricPotentialFixed::ElectricPotentialFixed( - const panzer::IntegrationRule& ir, const Teuchos::ParameterList& bc_params) - : _boundary_electric_potential("BOUNDARY_electric_potential", ir.dl_scalar) - , _boundary_grad_electric_potential("BOUNDARY_GRAD_electric_potential", - ir.dl_vector) - , _num_grad_dim(ir.spatial_dimension) - , _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) -{ - // Calculate the coefficients 'a' and 'b' for the linear time ramping - // sc(t) = a * t + b - _time_init = bc_params.isType("Time Initial") - ? bc_params.get( - "Time " - "Initial") - : 0.0; - _time_final = bc_params.isType("Time Final") - ? bc_params.get("Time Final") - : 1.0E-06; - const double dt = _time_final - _time_init; - const auto sc_final = bc_params.get("Final Value"); - const auto sc_init = bc_params.isType("Initial Value") - ? bc_params.get("Initial Value") - : sc_final; - _a_sc = (sc_final - sc_init) / dt; - _b_sc = sc_init - _a_sc * _time_init; - - // Add evaluated fields - this->addEvaluatedField(_boundary_electric_potential); - this->addEvaluatedField(_boundary_grad_electric_potential); - - // Add dependent fields - this->addDependentField(_grad_electric_potential); - - this->setName("Boundary State Electric Potential Fixed " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialFixed::evaluateFields( - typename Traits::EvalData workset) -{ - // Get time and make sure it only varies between '_time_init' and - // '_time_final' - _time = std::max(workset.time, _time_init); - _time = std::min(_time, _time_final); - - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialFixed::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_electric_potential.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Assign time-dependent boundary values - _boundary_electric_potential(cell, point) = _a_sc * _time + _b_sc; - - // Assign gradient. - for (int d = 0; d < _num_grad_dim; ++d) - { - _boundary_grad_electric_potential(cell, point, d) - = _grad_electric_potential(cell, point, d); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALFIXED_IMPL_HPP diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.cpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.cpp deleted file mode 100644 index 1ad8618..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp" -#include "VertexCFD_BoundaryState_ElectricPotentialInsulatingWall_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::ElectricPotentialInsulatingWall) diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp deleted file mode 100644 index b40e505..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_HPP -#define VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class ElectricPotentialInsulatingWall - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ElectricPotentialInsulatingWall(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField - _boundary_electric_potential; - PHX::MDField - _boundary_grad_electric_potential; - - private: - PHX::MDField - _electric_potential; - PHX::MDField - _grad_electric_potential; - PHX::MDField - _normals; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_HPP diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall_impl.hpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall_impl.hpp deleted file mode 100644 index ada8a86..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall_impl.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -ElectricPotentialInsulatingWall::ElectricPotentialInsulatingWall( - const panzer::IntegrationRule& ir) - : _boundary_electric_potential("BOUNDARY_electric_potential", ir.dl_scalar) - , _boundary_grad_electric_potential("BOUNDARY_GRAD_electric_potential", - ir.dl_vector) - , _electric_potential("electric_potential", ir.dl_scalar) - , _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) -{ - this->addDependentField(_electric_potential); - this->addEvaluatedField(_boundary_electric_potential); - this->addDependentField(_grad_electric_potential); - this->addEvaluatedField(_boundary_grad_electric_potential); - - this->addDependentField(_normals); - - this->setName("Boundary State Electric Potential Insulating Wall"); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialInsulatingWall::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(policy, *this, this->getName()); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialInsulatingWall::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _electric_potential.extent(1); - const int num_grad_dim = _normals.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Compute \grad(\phi) \cdot \vec{n} - scalar_type grad_phi_dot_n = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - grad_phi_dot_n += _grad_electric_potential(cell, point, dim) - * _normals(cell, point, dim); - } - - _boundary_electric_potential(cell, point) - = _electric_potential(cell, point); - - // Set and boundary gradients - for (int dim = 0; dim < num_grad_dim; ++dim) - { - _boundary_grad_electric_potential(cell, point, dim) - = _grad_electric_potential(cell, point, dim) - - grad_phi_dot_n * _normals(cell, point, dim); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_ELECTRICPOTENTIALINSULATINGWALL_IMPL_HPP diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.cpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.cpp deleted file mode 100644 index dbbc7f0..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::ElectricPotentialBoundaryStateFactory) diff --git a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp b/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp deleted file mode 100644 index 4556812..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/VertexCFD_ElectricPotentialBoundaryState_Factory.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_ELECTRICPOTENTIALBOUNDARYSTATE_FACTORY_HPP -#define VERTEXCFD_ELECTRICPOTENTIALBOUNDARYSTATE_FACTORY_HPP - -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp" -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class ElectricPotentialBoundaryStateFactory -{ - public: - static Teuchos::RCP> - create(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const Teuchos::ParameterList& /*user_params*/) - { - // Loop over boundary conditions - Teuchos::RCP> state; - bool found_model = false; - - if (bc_params.isType("Type")) - { - if (bc_params.get("Type") == "Fixed") - { - state = Teuchos::rcp( - new ElectricPotentialFixed(ir, bc_params)); - found_model = true; - } - - if (bc_params.get("Type") == "InsulatingWall") - { - state = Teuchos::rcp( - new ElectricPotentialInsulatingWall(ir)); - found_model = true; - } - } - - if (!found_model) - { - std::string msg = "\n\nBoundary state " - + bc_params.get("Type") - + " failed to build.\n"; - msg += "The boundary conditions implemented in VertexCFD\n"; - msg += "for the electric potential equation are:\n"; - msg += "Fixed,\n"; - msg += "InsulatingWall,\n"; - msg += "\n"; - throw std::runtime_error(msg); - } - - return state; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_ELECTRICPOTENTIALBOUNDARYSTATE_FACTORY_HPP diff --git a/src/induction_less_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt b/src/induction_less_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index 34549a3..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - TimeTransientElectricPotentialFixed - ElectricPotentialInsulatingWall - ) diff --git a/src/induction_less_mhd_solver/boundary_conditions/unit_test/doc/insulating_wall_reference.py b/src/induction_less_mhd_solver/boundary_conditions/unit_test/doc/insulating_wall_reference.py deleted file mode 100644 index 79e34bd..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/unit_test/doc/insulating_wall_reference.py +++ /dev/null @@ -1,23 +0,0 @@ -import numpy as np - -# 2-D case -normals = np.array([3.0, 6.0]) -ep_grad = np.array([0.1, 0.2]) - -ep_dot_n = np.dot(normals, ep_grad) -print(ep_dot_n) - -print("\n2-D case:") -print("x-boundary value: ", ep_grad[0] - ep_dot_n * normals[0]) -print("y-boundary value: ", ep_grad[1] - ep_dot_n * normals[1]) - -# 3-D case -normals = np.array([3.0, 6.0, 9.0]) -ep_grad = np.array([0.1, 0.2, 0.3]) - -ep_dot_n = np.dot(normals, ep_grad) - -print("\n3-D case:") -print("x-boundary value: ", ep_grad[0] - ep_dot_n * normals[0]) -print("y-boundary value: ", ep_grad[1] - ep_dot_n * normals[1]) -print("z-boundary value: ", ep_grad[2] - ep_dot_n * normals[2]) diff --git a/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstElectricPotentialInsulatingWall.cpp b/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstElectricPotentialInsulatingWall.cpp deleted file mode 100644 index f57bb63..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstElectricPotentialInsulatingWall.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialInsulatingWall.hpp" -#include - -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test - -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _electric_potential; - PHX::MDField - _grad_electric_potential; - PHX::MDField _normals; - - Dependencies(const panzer::IntegrationRule& ir) - : _electric_potential("electric_potential", ir.dl_scalar) - , _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(_electric_potential); - this->addEvaluatedField(_grad_electric_potential); - this->addEvaluatedField(_normals); - this->setName( - "Electric Potential Insulating Wall Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - _electric_potential.deep_copy(2.0); - - Kokkos::parallel_for( - "electric potential insulating wall test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _grad_electric_potential.extent(1); - const int num_grad_dim = _grad_electric_potential.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - for (int d = 0; d < num_grad_dim; ++d) - { - const int dqp = (qp + 1) * (d + 1); - _grad_electric_potential(c, qp, d) = dqp * 3.0; - _normals(c, qp, d) = dqp * 0.1; - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim) -{ - // Test fixture - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create evaluator. - const auto fixed_eval = Teuchos::rcp( - new BoundaryCondition::ElectricPotentialInsulatingWall( - *test_fixture.ir)); - test_fixture.registerEvaluator(fixed_eval); - - // Add required test fields. - test_fixture.registerTestField( - fixed_eval->_boundary_electric_potential); - test_fixture.registerTestField( - fixed_eval->_boundary_grad_electric_potential); - - // Evaluate variables. - test_fixture.evaluate(); - - // Check values. - const auto boundary_ep_result = test_fixture.getTestFieldData( - fixed_eval->_boundary_electric_potential); - const auto boundary_grad_ep_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_electric_potential); - - const int num_point = boundary_ep_result.extent(1); - const double ep_dot_n = num_grad_dim == 2 ? 1.5 : 4.2; - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(2.0, fieldValue(boundary_ep_result, 0, qp)); - - for (int d = 0; d < num_grad_dim; ++d) - { - const int dqp = (qp + 1) * (d + 1); - EXPECT_DOUBLE_EQ(dqp * 3.0 - ep_dot_n * dqp * 0.1, - fieldValue(boundary_grad_ep_result, 0, qp, d)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D residual -TEST(InsulatingWall2D, residual) -{ - testEval(2); -} - -// 2-D jacobian -TEST(InsulatingWall2D, jacobian) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D residual -TEST(InsulatingWall3D, residual) -{ - testEval(3); -} - -// 3-D jacobian -TEST(InsulatingWall3D, jacobian) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstTimeTransientElectricPotentialFixed.cpp b/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstTimeTransientElectricPotentialFixed.cpp deleted file mode 100644 index 7eff867..0000000 --- a/src/induction_less_mhd_solver/boundary_conditions/unit_test/tstTimeTransientElectricPotentialFixed.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "induction_less_mhd_solver/boundary_conditions/VertexCFD_BoundaryState_ElectricPotentialFixed.hpp" -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test - -{ -//---------------------------------------------------------------------------// -// Input type -enum class InputType -{ - steady, - pastFinalTime, - transient, - preInitialTime -}; - -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _electric_potential; - PHX::MDField - _grad_electric_potential; - - Dependencies(const panzer::IntegrationRule& ir) - : _electric_potential("electric_potential", ir.dl_scalar) - , _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - { - this->addEvaluatedField(_electric_potential); - this->addEvaluatedField(_grad_electric_potential); - this->setName( - "Time Transient Electric Potential Fixed Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - _electric_potential.deep_copy(2.0); - - Kokkos::parallel_for( - "time transient electric potential fixed test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = _grad_electric_potential.extent(1); - const int num_grad_dim = _grad_electric_potential.extent(2); - for (int qp = 0; qp < num_point; ++qp) - { - for (int d = 0; d < num_grad_dim; ++d) - { - const int dqp = (qp + 1) * (d + num_point + 1); - _grad_electric_potential(c, qp, d) = dqp * 3.0; - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim, const InputType input_type) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create the param list to initialize the evaluator - Teuchos::ParameterList bc_params; - double time = 0.0; - double exp_value = 0.0; - switch (input_type) - { - case (InputType::steady): - exp_value = 2.0; - bc_params.set("Final Value", exp_value); - break; - case (InputType::pastFinalTime): - exp_value = 2.0; - bc_params.set("Final Value", exp_value); - bc_params.set("Initial Value", 0.5); - time = 1.5; - bc_params.set("Time Final", 1.0); - bc_params.set("Time Initial", 0.1); - break; - case (InputType::transient): - exp_value = 3.5; - bc_params.set("Final Value", 3.0); - bc_params.set("Initial Value", 4.0); - time = 1.5; - bc_params.set("Time Final", 2.0); - bc_params.set("Time Initial", 1.0); - break; - case (InputType::preInitialTime): - exp_value = 3.0; - bc_params.set("Final Value", 4.0); - bc_params.set("Initial Value", exp_value); - time = 0.5; - bc_params.set("Time Final", 2.0); - bc_params.set("Time Initial", 1.0); - break; - } - - // Create fixed evaluator. - auto fixed_eval = Teuchos::rcp( - new BoundaryCondition::ElectricPotentialFixed( - *test_fixture.ir, bc_params)); - test_fixture.registerEvaluator(fixed_eval); - - // Add required test fields. - test_fixture.registerTestField( - fixed_eval->_boundary_electric_potential); - test_fixture.registerTestField( - fixed_eval->_boundary_grad_electric_potential); - - // Set time - test_fixture.setTime(time); - - // Evaluate time transient fixed BC. - test_fixture.evaluate(); - - // Check the time transient fixed BC. - const auto boundary_ep_result = test_fixture.getTestFieldData( - fixed_eval->_boundary_electric_potential); - const auto boundary_grad_ep_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_electric_potential); - - const int num_point = boundary_ep_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_value, fieldValue(boundary_ep_result, 0, qp)); - - for (int d = 0; d < num_grad_dim; ++d) - { - const int dqp = (qp + 1) * (d + num_point + 1); - EXPECT_DOUBLE_EQ(dqp * 3.0, - fieldValue(boundary_grad_ep_result, 0, qp, d)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D: time transient fixed - steady -template -void testTimeTransientElectricPotentialFixedSteady2D() -{ - testEval(2, InputType::steady); -} - -// 2-D time transient fixed residual -TEST(TimeTransientElectricPotentialFixedSteady2D, residual) -{ - testTimeTransientElectricPotentialFixedSteady2D(); -} - -// 2-D time transient fixed jacobian -TEST(TimeTransientElectricPotentialFixedSteady2D, jacobian) -{ - testTimeTransientElectricPotentialFixedSteady2D(); -} - -//---------------------------------------------------------------------------// -// 2-D time transient fixed - time > time_final -template -void testTimeTransientElectricPotentialFixedTimeFinal2D() -{ - testEval(2, InputType::pastFinalTime); -} - -// 2-D time transient fixed residual -TEST(TimeTransientElectricPotentialFixedTimeFinal2D, residual) -{ - testTimeTransientElectricPotentialFixedTimeFinal2D(); -} - -// 2-D time transient fixed jacobian -TEST(TimeTransientElectricPotentialFixedTimeFinal2D, jacobian) -{ - testTimeTransientElectricPotentialFixedTimeFinal2D(); -} - -//---------------------------------------------------------------------------// -// 2-D time transient fixed - time < time_init -template -void testTimeTransientElectricPotentialFixedTimeInit2D() -{ - testEval(2, InputType::preInitialTime); -} - -// 2-D time transient fixed residual -TEST(TimeTransientElectricPotentialFixedTimeInit2D, residual) -{ - testTimeTransientElectricPotentialFixedTimeInit2D(); -} - -// 2-D time transient fixed jacobian -TEST(TimeTransientElectricPotentialFixedTimeInit2D, jacobian) -{ - testTimeTransientElectricPotentialFixedTimeInit2D(); -} - -//---------------------------------------------------------------------------// -// 2-D time transient fixed - time_init < time < time_final. The expected -// values should be the average value between the final and the initial values. -template -void testTimeTransientElectricPotentialFixedTimeIntermediate2D() -{ - testEval(2, InputType::transient); -} - -// 2-D time transient fixed residual -TEST(TimeTransientElectricPotentialFixedTimeIntermediate2D, residual) -{ - testTimeTransientElectricPotentialFixedTimeIntermediate2D< - panzer::Traits::Residual>(); -} - -// 2-D time transient fixed jacobian -TEST(TimeTransientElectricPotentialFixedTimeIntermediate2D, jacobian) -{ - testTimeTransientElectricPotentialFixedTimeIntermediate2D< - panzer::Traits::Jacobian>(); -} - -//---------------------------------------------------------------------------// -// 3-D time transient fixed - time_init < time < time_final. The expected -// values should be the average value between the final and the initial values -template -void testTimeTransientElectricPotentialFixedTimeIntermediate3D() -{ - testEval(3, InputType::transient); -} - -// 3-D time transient fixed residual -TEST(TimeTransientElectricPotentialFixedTimeIntermediate3D, residual) -{ - testTimeTransientElectricPotentialFixedTimeIntermediate3D< - panzer::Traits::Residual>(); -} - -// 3-D time transient fixed jacobian -TEST(TimeTransientElectricPotentialFixedTimeIntermediate3D, jacobian) -{ - testTimeTransientElectricPotentialFixedTimeIntermediate2D< - panzer::Traits::Jacobian>(); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.cpp deleted file mode 100644 index ca0e730..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ElectricCurrentDensity.hpp" -#include "VertexCFD_Closure_ElectricCurrentDensity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::ElectricCurrentDensity) diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp deleted file mode 100644 index 987c831..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_HPP -#define VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Electric current density -//---------------------------------------------------------------------------// -template -class ElectricCurrentDensity : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int field_size = 3; - - ElectricCurrentDensity( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _electric_current_density; - - private: - PHX::MDField - _grad_electric_potential; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, - field_size> - _ext_magn_field; - - double _sigma; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity_impl.hpp deleted file mode 100644 index 53f65b2..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity_impl.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ElectricCurrentDensity::ElectricCurrentDensity( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - , _sigma(fluid_prop.constantElectricalConductivity()) -{ - // Evaluated fields - Utils::addEvaluatedVectorField(*this, - ir.dl_scalar, - _electric_current_density, - "electric_current_density_"); - - // Dependent fields - this->addDependentField(_grad_electric_potential); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _ext_magn_field, "external_magnetic_field_"); - - this->setName("Electric Potential Electric Current Density " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ElectricCurrentDensity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ElectricCurrentDensity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _electric_current_density[0].extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Electric current density: 2-D case - // x-component - _electric_current_density[0](cell, point) - = -_sigma * _grad_electric_potential(cell, point, 0); - _electric_current_density[0](cell, point) - += _sigma * _velocity[1](cell, point) - * _ext_magn_field[2](cell, point); - // y-component - _electric_current_density[1](cell, point) - = -_sigma * _grad_electric_potential(cell, point, 1); - _electric_current_density[1](cell, point) - += -_sigma * _velocity[0](cell, point) - * _ext_magn_field[2](cell, point); - - // Electric current density: 3-D case - if (num_space_dim == 3) - { - // x-component - _electric_current_density[0](cell, point) - -= _sigma * _velocity[2](cell, point) - * _ext_magn_field[1](cell, point); - // y-component - _electric_current_density[1](cell, point) - += _sigma * _velocity[2](cell, point) - * _ext_magn_field[0](cell, point); - // z-component - _electric_current_density[2](cell, point) - = -_sigma * _grad_electric_potential(cell, point, 2); - _electric_current_density[2](cell, point) - += _sigma - * (_velocity[0](cell, point) - * _ext_magn_field[1](cell, point) - - _velocity[1](cell, point) - * _ext_magn_field[0](cell, point)); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_ELECTRICCURRENTDENSITY_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.cpp deleted file mode 100644 index 3e4b739..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp" -#include "VertexCFD_Closure_ElectricPotentialCrossProductFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::ElectricPotentialCrossProductFlux) diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp deleted file mode 100644 index 8c9eaa6..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_HPP -#define VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension cross product flux evaluation for electric potential -// equation. -//---------------------------------------------------------------------------// -template -class ElectricPotentialCrossProductFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int field_size = 3; - - ElectricPotentialCrossProductFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix = "", - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _electric_potential_flux; - - private: - PHX::MDField - _grad_electric_potential; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, - field_size> - _ext_magn_field; - - double _sigma; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux_impl.hpp deleted file mode 100644 index 4526331..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux_impl.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ElectricPotentialCrossProductFlux:: - ElectricPotentialCrossProductFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix, - const std::string& field_prefix) - : _electric_potential_flux( - flux_prefix + "ELECTRIC_POTENTIAL_FLUX_electric_potential_equation", - ir.dl_vector) - , _sigma(fluid_prop.constantElectricalConductivity()) -{ - // Evaluated fields - this->addContributedField(_electric_potential_flux); - - // Dependent fields - Utils::addDependentVectorField( - *this, ir.dl_scalar, _velocity, field_prefix + "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _ext_magn_field, "external_magnetic_field_"); - - this->setName("Electric Potential Cross Product Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialCrossProductFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialCrossProductFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _electric_potential_flux.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // 2-D case (x- and y-components only) - _electric_potential_flux(cell, point, 0) - += _sigma * _velocity[1](cell, point) - * _ext_magn_field[2](cell, point); - _electric_potential_flux(cell, point, 1) - -= _sigma * _velocity[0](cell, point) - * _ext_magn_field[2](cell, point); - - // 3-D case - if (num_space_dim == 3) - { - // x-component - _electric_potential_flux(cell, point, 0) - -= _sigma * _velocity[2](cell, point) - * _ext_magn_field[1](cell, point); - // y-component - _electric_potential_flux(cell, point, 1) - += _sigma * _velocity[2](cell, point) - * _ext_magn_field[0](cell, point); - // z-component - _electric_potential_flux(cell, point, 2) - += _sigma - * (_velocity[0](cell, point) - * _ext_magn_field[1](cell, point) - - _velocity[1](cell, point) - * _ext_magn_field[0](cell, point)); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELECTRICPOTENTIALCROSSPRODUCTFLUX_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.cpp deleted file mode 100644 index 93e62a0..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp" -#include "VertexCFD_Closure_ElectricPotentialDiffusionFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::ElectricPotentialDiffusionFlux) diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp deleted file mode 100644 index 47ceacf..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_HPP -#define VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Multi-dimension diffusion flux evaluation for electric potential equation. -//---------------------------------------------------------------------------// -template -class ElectricPotentialDiffusionFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ElectricPotentialDiffusionFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix = "", - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField - _electric_potential_flux; - - private: - PHX::MDField - _grad_electric_potential; - - double _sigma; - int _num_grad_dim; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux_impl.hpp deleted file mode 100644 index 6672a6d..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux_impl.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -ElectricPotentialDiffusionFlux::ElectricPotentialDiffusionFlux( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const std::string& flux_prefix, - const std::string& gradient_prefix) - : _electric_potential_flux( - flux_prefix + "ELECTRIC_POTENTIAL_FLUX_electric_potential_equation", - ir.dl_vector) - , _grad_electric_potential(gradient_prefix + "GRAD_electric_potential", - ir.dl_vector) - , _sigma(fluid_prop.constantElectricalConductivity()) - , _num_grad_dim(ir.spatial_dimension) -{ - // Evaluated fields - this->addEvaluatedField(_electric_potential_flux); - - // Dependent fields - this->addDependentField(_grad_electric_potential); - - this->setName("Electric Potential Diffusion Flux " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialDiffusionFlux::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void ElectricPotentialDiffusionFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _electric_potential_flux.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < _num_grad_dim; ++dim) - { - _electric_potential_flux(cell, point, dim) - = -_sigma * _grad_electric_potential(cell, point, dim); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_ELECTRICPOTENTIALDIFFUSIONFLUX_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.cpp deleted file mode 100644 index 446ac82..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_HartmannProblemExact.hpp" -#include "VertexCFD_Closure_HartmannProblemExact_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::HartmannProblemExact) diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp deleted file mode 100644 index b87bca7..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_HPP -#define VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Hartmann Problem Exact Solution -//---------------------------------------------------------------------------// -template -class HartmannProblemExact : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - HartmannProblemExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _exact_lagrange_pressure; - Kokkos::Array, - num_space_dim> - _exact_velocity; - PHX::MDField _exact_elec_pot; - - private: - double _sigma; - double _rho; - double _nu; - double _L; - double _B; - double _M; - int _ir_degree; - int _ir_index; - - PHX::MDField _ip_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact_impl.hpp deleted file mode 100644 index cb363a4..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact_impl.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_IMPL_HPP -#define VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_IMPL_HPP - -#include - -#include "Panzer_GlobalIndexer.hpp" -#include "Panzer_PureBasis.hpp" -#include "Panzer_Workset_Utilities.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -HartmannProblemExact::HartmannProblemExact( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _exact_lagrange_pressure("Exact_lagrange_pressure", ir.dl_scalar) - , _exact_elec_pot("Exact_electric_potential", ir.dl_scalar) - , _sigma(fluid_prop.constantElectricalConductivity()) - , _rho(fluid_prop.constantDensity()) - , _nu(fluid_prop.constantKinematicViscosity()) - , _L(user_params.get("Hartmann Solution Half-Width Channel")) - , _B(0.0) - , _ir_degree(ir.cubature_degree) -{ - // Evaluated fields - this->addEvaluatedField(_exact_lagrange_pressure); - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _exact_velocity, "Exact_velocity_"); - this->addEvaluatedField(_exact_elec_pot); - - // Get external magnetic vector - using std::sqrt; - const auto ext_magn_vct - = user_params.get>("External Magnetic Field"); - for (int dim = 0; dim < 3; ++dim) - { - _B += ext_magn_vct[dim] * ext_magn_vct[dim]; - } - _B = sqrt(_B); - - // Hartmann Number - _M = _B * _L * std::sqrt(_sigma / (_rho * _nu)); - - // Closure model name - this->setName("Exact Solution Hartmann Problem"); -} - -//---------------------------------------------------------------------------// -template -void HartmannProblemExact::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _ir_index = panzer::getIntegrationRuleIndex(_ir_degree, (*sd.worksets_)[0]); -} - -//---------------------------------------------------------------------------// -template -void HartmannProblemExact::evaluateFields( - typename Traits::EvalData workset) -{ - _ip_coords = workset.int_rules[_ir_index]->ip_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void HartmannProblemExact::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _exact_elec_pot.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Note: an analytical solution is only available for - // the velocity and the electric potential. - _exact_lagrange_pressure(cell, point) = 0.0; - using std::cosh; - const double y = _ip_coords(cell, point, 1); - _exact_velocity[0](cell, point) = (cosh(_M) - cosh(_M * y / _L)) - / (cosh(_M) - 1.0); - _exact_velocity[1](cell, point) = 0.0; - if (num_space_dim == 3) - _exact_velocity[2](cell, point) = 0.0; - _exact_elec_pot(cell, point) = 0.0; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_HARTMANNPROBLEMEXACT_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.cpp deleted file mode 100644 index c71bd20..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_LorentzForce.hpp" -#include "VertexCFD_Closure_LorentzForce_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::LorentzForce) diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp deleted file mode 100644 index 6fb63e9..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_LORENTZFORCE_HPP -#define VERTEXCFD_CLOSURE_LORENTZFORCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Lorentz force -//---------------------------------------------------------------------------// -template -class LorentzForce : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int field_size = 3; - - LorentzForce(const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - Kokkos::Array, - num_space_dim> - _lorentz_force; - - private: - double _sigma; - PHX::MDField - _grad_electric_potential; - Kokkos::Array, - num_space_dim> - _electric_current_density; - Kokkos::Array, - num_space_dim> - _velocity; - Kokkos::Array, - field_size> - _ext_magn_field; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_LORENTZFORCE_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce_impl.hpp deleted file mode 100644 index 5ade136..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce_impl.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_LORENTZFORCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_LORENTZFORCE_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -LorentzForce::LorentzForce( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _sigma(fluid_prop.constantElectricalConductivity()) - , _grad_electric_potential("GRAD_electric_potential", ir.dl_vector) -{ - // Evaluated fields - Utils::addEvaluatedVectorField( - *this, ir.dl_scalar, _lorentz_force, "VOLUMETRIC_SOURCE_momentum_"); - - // Dependent fields - this->addDependentField(_grad_electric_potential); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - Utils::addDependentVectorField( - *this, ir.dl_scalar, _ext_magn_field, "external_magnetic_field_"); - - this->setName("Electric Potential Lorentz Force " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void LorentzForce::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void LorentzForce::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_electric_potential.extent(1); - const int num_grad_dim = _grad_electric_potential.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Cross product term - _lorentz_force[0](cell, point) - = -_grad_electric_potential(cell, point, 1) - * _ext_magn_field[2](cell, point); - - _lorentz_force[1](cell, point) - = _grad_electric_potential(cell, point, 0) - * _ext_magn_field[2](cell, point); - - if (num_grad_dim == 3) - { - // x-component - _lorentz_force[0](cell, point) - += _grad_electric_potential(cell, point, 2) - * _ext_magn_field[1](cell, point); - // y-component - _lorentz_force[1](cell, point) - -= _grad_electric_potential(cell, point, 2) - * _ext_magn_field[0](cell, point); - // z-component - _lorentz_force[2](cell, point) - = -_grad_electric_potential(cell, point, 0) - * _ext_magn_field[1](cell, point) - + _grad_electric_potential(cell, point, 1) - * _ext_magn_field[0](cell, point); - } - - // Compute B norm and \vec{u} \cdot \vec{B}. NOTE: the external - // magnetic field is assumed of the same dimension as the mesh in - // this implementation. - scalar_type B2 = 0.0; - scalar_type B_dot_u = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - B2 += _ext_magn_field[dim](cell, point) - * _ext_magn_field[dim](cell, point); - B_dot_u += _ext_magn_field[dim](cell, point) - * _velocity[dim](cell, point); - } - - // Adding dot product terms - for (int dim = 0; dim < num_grad_dim; ++dim) - { - _lorentz_force[dim](cell, point) - += _ext_magn_field[dim](cell, point) * B_dot_u; - _lorentz_force[dim](cell, point) - -= B2 * _velocity[dim](cell, point); - _lorentz_force[dim](cell, point) *= _sigma; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_LORENTZFORCE_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp deleted file mode 100644 index 17b232c..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_HPP -#define VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class InductionlessFactory -{ - public: - void buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& closure_params, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators); -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_HPP diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian2d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian2d.cpp deleted file mode 100644 index 243cd35..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian3d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian3d.cpp deleted file mode 100644 index 5dcd2f0..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Hessian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian2d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian2d.cpp deleted file mode 100644 index 223675b..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian3d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian3d.cpp deleted file mode 100644 index a692221..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Jacobian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual2d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual2d.cpp deleted file mode 100644 index 1129d98..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual3d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual3d.cpp deleted file mode 100644 index 6c2ac12..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Residual3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent2d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent2d.cpp deleted file mode 100644 index 088d5ee..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent3d.cpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent3d.cpp deleted file mode 100644 index 9f3bc2c..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_Tangent3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_InductionlessClosureModelFactory.hpp" -#include "VertexCFD_InductionlessClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel:: - InductionlessFactory; diff --git a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_impl.hpp b/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_impl.hpp deleted file mode 100644 index 132a6e7..0000000 --- a/src/induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory_impl.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_IMPL_HPP -#define VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_IMPL_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_InductionlessClosureModelFactory.hpp" - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -void InductionlessFactory::buildClosureModel( - const std::string& closure_type, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const Teuchos::ParameterList& /**closure_params**/, - bool& found_model, - std::string& error_msg, - Teuchos::RCP>>> - evaluators) -{ - // Define local variables - constexpr int num_space_dim = NumSpaceDim; - Teuchos::RCP> eval; - - // Fluid properties - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_buoyancy_source - = user_params.isType("Build Buoyancy Source") - ? user_params.get("Build Buoyancy Source") - : false; - bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Buoyancy Source", build_buoyancy_source); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params - = FluidProperties::ConstantFluidProperties(fluid_prop_list); - - // Closure models - if (closure_type == "ElectricCurrentDensity") - { - auto eval = Teuchos::rcp( - new ElectricCurrentDensity( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "ElectricPotentialCrossProductFlux") - { - auto eval = Teuchos::rcp( - new ElectricPotentialCrossProductFlux( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "ElectricPotentialDiffusionFlux") - { - auto eval = Teuchos::rcp( - new ElectricPotentialDiffusionFlux( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "HartmannProblemExact") - { - auto eval = Teuchos::rcp( - new HartmannProblemExact( - *ir, incompressible_fluidprop_params, user_params)); - evaluators->push_back(eval); - found_model = true; - } - - if (closure_type == "LorentzForce") - { - auto eval = Teuchos::rcp( - new LorentzForce( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval); - found_model = true; - } - - // Initialize 'error_msg' with list of closure models for Inductionless - // MHD equations - error_msg = "ElectricCurrentDensity\n"; - error_msg += "ElectricPotentialCrossProductFlux\n"; - error_msg += "ElectricPotentialDiffusionFlux\n"; - error_msg += "ExternalMagneticField\n"; - error_msg += "HartmannProblemExact\n"; - error_msg += "LorentzForce\n"; -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INDUCTIONLESSCLOSUREMODELFACTORY_IMPL_HPP diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/CMakeLists.txt b/src/induction_less_mhd_solver/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index 906b85b..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - ElectricPotentialDiffusionFlux - ElectricPotentialCrossProductFlux - ElectricCurrentDensity - LorentzForce - HartmannProblem - ) diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_cross_product_flux.py b/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_cross_product_flux.py deleted file mode 100644 index 7921b93..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_cross_product_flux.py +++ /dev/null @@ -1,18 +0,0 @@ -import numpy as np - -# electrical conductivity -sigma = 6.25 - -# 2D -B = np.array([0.0, 0.0, 0.3]) -v = np.array([0.5, 1.5, 0.0]) - -flux = sigma * np.cross(v, B) -print("2-D flux:", flux) - -# 3D -B = np.array([1.1, 2.0, -0.3]) -v = np.array([0.5, 1.5, 2.5]) - -flux = sigma * np.cross(v, B) -print("3-D flux:", flux) diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_current_density_reference.py b/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_current_density_reference.py deleted file mode 100644 index 4d2c7f6..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_current_density_reference.py +++ /dev/null @@ -1,20 +0,0 @@ -import numpy as np - -# electrical conductivity -sigma = 6.25 - -# 2D -grad_phi = np.array([0.1, -0.2, 0.0]) -B = np.array([0.0, 0.0, 0.3]) -v = np.array([0.5, 1.5, 0.0]) - -J = sigma * (np.cross(v, B) - grad_phi) -print("2-D J:", J) - -# 3D -grad_phi = np.array([0.1, -0.2, 0.3]) -B = np.array([1.1, 2.0, -0.3]) -v = np.array([0.5, 1.5, 2.5]) - -J = sigma * (np.cross(v, B) - grad_phi) -print("3-D J:", J) diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_lorentz_force_reference.py b/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_lorentz_force_reference.py deleted file mode 100644 index 02d2fb6..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_equ_lorentz_force_reference.py +++ /dev/null @@ -1,25 +0,0 @@ -import numpy as np - -# Dependent variables -sigma = 3.0 -B = np.array([1.1, 2.0, -0.3]) -vel = np.array([0.1, -0.2, 0.3]) - - -# Function to compute Lorentz force -def lorentz_force(sigma, grad_phi, B, dim): - lf = -np.cross(grad_phi, B)[0:dim] - print(lf) - lf += np.dot(B[0:dim], vel[0:dim]) * B[0:dim] - lf -= np.dot(B[0:dim], B[0:dim]) * vel[0:dim] - lf *= sigma - return lf - - -# 2D -grad_phi = np.array([0.6, 0.4, 0.0]) -print("2-D Florentz:", lorentz_force(sigma, grad_phi, B, 2)) - -# 3D -grad_phi = np.array([0.6, 0.4, 0.5]) -print("3-D Florentz:", lorentz_force(sigma, grad_phi, B, 3)) diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_hartmann_problem.py b/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_hartmann_problem.py deleted file mode 100644 index 6eb2961..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/doc/elec_pot_hartmann_problem.py +++ /dev/null @@ -1,18 +0,0 @@ -import numpy as np - - -def hartmann_pb_exact(M, L, y): - return (np.cosh(M) - np.cosh(M * y / L)) / (np.cosh(M) - 1.0) - - -B = np.array([1.5, 2.0, 3.0]) -B_magn = np.linalg.norm(B) -L = 2.5 -sigma = 3.5 -rho = 4.0 -nu = 4.5 -M = B_magn * L * np.sqrt(sigma / (rho * nu)) - -# output value -y = 0.5 -print("Exact value:", hartmann_pb_exact(M, L, y)) diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricCurrentDensity.cpp b/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricCurrentDensity.cpp deleted file mode 100644 index 3f7ca55..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricCurrentDensity.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricCurrentDensity.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - int num_grad_dim; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField velocity_0; - PHX::MDField velocity_1; - PHX::MDField velocity_2; - PHX::MDField ext_magn_field_0; - PHX::MDField ext_magn_field_1; - PHX::MDField ext_magn_field_2; - PHX::MDField - grad_electric_potential; - - Dependencies(const panzer::IntegrationRule& ir) - : num_grad_dim(ir.spatial_dimension) - , velocity_0("velocity_0", ir.dl_scalar) - , velocity_1("velocity_1", ir.dl_scalar) - , velocity_2("velocity_2", ir.dl_scalar) - , ext_magn_field_0("external_magnetic_field_0", ir.dl_scalar) - , ext_magn_field_1("external_magnetic_field_1", ir.dl_scalar) - , ext_magn_field_2("external_magnetic_field_2", ir.dl_scalar) - , grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - { - this->addEvaluatedField(velocity_0); - this->addEvaluatedField(velocity_1); - this->addEvaluatedField(velocity_2); - this->addEvaluatedField(ext_magn_field_0); - this->addEvaluatedField(ext_magn_field_1); - this->addEvaluatedField(ext_magn_field_2); - this->addEvaluatedField(grad_electric_potential); - this->setName( - "Electric Potential Electric Current Density Unit Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "electric potential electric current density test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = velocity_0.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - velocity_0(c, qp) = 0.5; - velocity_1(c, qp) = 1.5; - grad_electric_potential(c, qp, 0) = 0.1; - grad_electric_potential(c, qp, 1) = -0.2; - if (num_grad_dim == 3) - { - velocity_2(c, qp) = 2.5; - grad_electric_potential(c, qp, 2) = 0.3; - ext_magn_field_0(c, qp) = 1.1; - ext_magn_field_1(c, qp) = 2.0; - ext_magn_field_2(c, qp) = -0.3; - } - else - { - velocity_2(c, qp) = _nanval; - ext_magn_field_0(c, qp) = 0.0; - ext_magn_field_1(c, qp) = 0.0; - ext_magn_field_2(c, qp) = 0.3; - } - } - } -}; - -template -void testEval() -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize dependents - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Build Inductionless MHD Equation", true); - fluid_prop_list.set("Electrical conductivity", 6.25); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - const auto eval = Teuchos::rcp( - new ClosureModel:: - ElectricCurrentDensity( - ir, fluid_prop)); - - // Register - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - { - test_fixture.registerTestField( - eval->_electric_current_density[dim]); - } - - test_fixture.evaluate(); - - const int num_point = ir.num_points; - - // Assert values - double exp_values[num_space_dim]; - if (num_space_dim == 2) - { - exp_values[0] = 2.1875; - exp_values[1] = 0.3125; - } - else - { - exp_values[0] = -34.6875; - exp_values[1] = 19.375; - exp_values[2] = -5.9375; - } - - for (int qp = 0; qp < num_point; ++qp) - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto Jdim = test_fixture.getTestFieldData( - eval->_electric_current_density[dim]); - EXPECT_NEAR(exp_values[dim], fieldValue(Jdim, 0, qp), 1.0e-15); - } -} - -//-----------------------------------------------------------------// -TEST(ElectricCurrentDensity2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricCurrentDensity2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricCurrentDensity3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricCurrentDensity3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.0); - test_fixture.type_name = "ElectricCurrentDensity"; - test_fixture.eval_name = "Electric Potential Electric Current Density " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::ElectricCurrentDensity, - num_space_dim>(); -} - -TEST(ElectricCurrentDensity_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(ElectricCurrentDensity_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialCrossProductFlux.cpp b/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialCrossProductFlux.cpp deleted file mode 100644 index 352b6ec..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialCrossProductFlux.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialCrossProductFlux.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - int num_grad_dim; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField velocity_0; - PHX::MDField velocity_1; - PHX::MDField velocity_2; - PHX::MDField ext_magn_field_0; - PHX::MDField ext_magn_field_1; - PHX::MDField ext_magn_field_2; - - Dependencies(const panzer::IntegrationRule& ir, std::string field_prefix) - : num_grad_dim(ir.spatial_dimension) - , velocity_0(field_prefix + "velocity_0", ir.dl_scalar) - , velocity_1(field_prefix + "velocity_1", ir.dl_scalar) - , velocity_2(field_prefix + "velocity_2", ir.dl_scalar) - , ext_magn_field_0("external_magnetic_field_0", ir.dl_scalar) - , ext_magn_field_1("external_magnetic_field_1", ir.dl_scalar) - , ext_magn_field_2("external_magnetic_field_2", ir.dl_scalar) - { - this->addEvaluatedField(velocity_0); - this->addEvaluatedField(velocity_1); - this->addEvaluatedField(velocity_2); - this->addEvaluatedField(ext_magn_field_0); - this->addEvaluatedField(ext_magn_field_1); - this->addEvaluatedField(ext_magn_field_2); - this->setName( - "Electric Potential Cross Product Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "electric potential cross product flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = velocity_0.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - velocity_0(c, qp) = 0.5; - velocity_1(c, qp) = 1.5; - if (num_grad_dim == 3) - { - velocity_2(c, qp) = 2.5; - ext_magn_field_0(c, qp) = 1.1; - ext_magn_field_1(c, qp) = 2.0; - ext_magn_field_2(c, qp) = -0.3; - } - else - { - velocity_2(c, qp) = _nanval; - ext_magn_field_0(c, qp) = 0.0; - ext_magn_field_1(c, qp) = 0.0; - ext_magn_field_2(c, qp) = 0.3; - } - } - } -}; - -template -void testEval(const std::string field_prefix = "") -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize dependents - const auto deps - = Teuchos::rcp(new Dependencies(ir, field_prefix)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Build Inductionless MHD Equation", true); - fluid_prop_list.set("Electrical conductivity", 6.25); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - const auto eval = Teuchos::rcp( - new ClosureModel::ElectricPotentialCrossProductFlux( - ir, fluid_prop)); - - // Register - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_electric_potential_flux); - - test_fixture.evaluate(); - - const auto ind_flux = test_fixture.getTestFieldData( - eval->_electric_potential_flux); - - const int num_point = ir.num_points; - - // Assert values - double exp_values[num_space_dim]; - if (num_space_dim == 2) - { - exp_values[0] = 2.8125; - exp_values[1] = -0.9375; - } - else - { - exp_values[0] = -34.0625; - exp_values[1] = 18.125; - exp_values[2] = -4.0625; - } - - for (int qp = 0; qp < num_point; ++qp) - for (int dim = 0; dim < num_space_dim; ++dim) - { - EXPECT_NEAR( - exp_values[dim], fieldValue(ind_flux, 0, qp, dim), 1.0e-15); - } -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialCrossProduct2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialCrossProduct2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialCrossProduct3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialCrossProduct3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.0); - test_fixture.type_name = "ElectricPotentialCrossProductFlux"; - test_fixture.eval_name = "Electric Potential Cross Product Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::ElectricPotentialCrossProductFlux, - num_space_dim>(); -} - -TEST(ElectricPotentialCrossProduct_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(ElectricPotentialCrossProduct_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialDiffusionFlux.cpp b/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialDiffusionFlux.cpp deleted file mode 100644 index e142247..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/tstElectricPotentialDiffusionFlux.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_ElectricPotentialDiffusionFlux.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField - grad_electric_potential; - - Dependencies(const panzer::IntegrationRule& ir, std::string grad_prefix) - : grad_electric_potential(grad_prefix + "GRAD_electric_potential", - ir.dl_vector) - { - this->addEvaluatedField(grad_electric_potential); - this->setName( - "Electric Potential Diffusion Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "electric potential diffusion flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_electric_potential.extent(1); - const int num_grad_dim = grad_electric_potential.extent(2); - for (int qp = 0; qp < num_point; ++qp) - for (int dim = 0; dim < num_grad_dim; ++dim) - grad_electric_potential(c, qp, dim) = 1.5 * (qp + 1) - * (dim + 1); - } -}; - -//-----------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim, - const std::string flux_prefix = "", - const std::string grad_prefix = "") -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize dependents - const auto deps = Teuchos::rcp(new Dependencies(ir, grad_prefix)); - test_fixture.registerEvaluator(deps); - - // Initialize closure model constructor - const double sigma = 3.0; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Build Inductionless MHD Equation", true); - fluid_prop_list.set("Electrical conductivity", sigma); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - const auto eval = Teuchos::rcp( - new ClosureModel::ElectricPotentialDiffusionFlux( - ir, fluid_prop, flux_prefix, grad_prefix)); - - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_electric_potential_flux); - test_fixture.evaluate(); - - const auto diff_flux = test_fixture.getTestFieldData( - eval->_electric_potential_flux); - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_grad_dim; dim++) - { - const auto exp_value = sigma * 1.5 * (qp + 1) * (dim + 1); - EXPECT_EQ(-exp_value, fieldValue(diff_flux, 0, qp, dim)); - } - } -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion2D, residual_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion2D, jacobian_test) -{ - testEval(2); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion3D, residual_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion3D, jacobian_test) -{ - testEval(3); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion3DPrefix, residual_test) -{ - testEval(3, "BAR_", "BOO_"); -} - -//-----------------------------------------------------------------// -TEST(ElectricPotentialDiffusion3DPrefix, jacobian_test) -{ - testEval(3, "BAR_", "BOO_"); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.0); - test_fixture.type_name = "ElectricPotentialDiffusionFlux"; - test_fixture.eval_name = "Electric Potential Diffusion Flux " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::ElectricPotentialDiffusionFlux, - num_space_dim>(); -} - -TEST(ElectricPotentialFlux_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(ElectricPotentialFlux_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/tstHartmannProblem.cpp b/src/induction_less_mhd_solver/closure_models/unit_test/tstHartmannProblem.cpp deleted file mode 100644 index e817dfa..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/tstHartmannProblem.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_HartmannProblemExact.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -void testEval() -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Set non-trivial y values - test_fixture.int_values->ip_coordinates(0, 0, 1) = 0.5; - - // Initialize class object to test - Teuchos::ParameterList user_params; - user_params.set("Hartmann Solution Half-Width Channel", 2.5); - Teuchos::Array ext_magn_vct(3); - ext_magn_vct[0] = 1.5; - ext_magn_vct[1] = 2.0; - ext_magn_vct[2] = 3.0; - user_params.set("External Magnetic Field", ext_magn_vct); - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 4.5); - fluid_prop_list.set("Density", 4.0); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Build Inductionless MHD Equation", true); - fluid_prop_list.set("Electrical conductivity", 3.5); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - const auto eval = Teuchos::rcp( - new ClosureModel:: - HartmannProblemExact( - ir, fluid_prop, user_params)); - - // Register - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_exact_velocity[dim]); - - test_fixture.evaluate(); - const auto value - = test_fixture.getTestFieldData(eval->_exact_velocity[0]); - - // Assert values - const int num_point = ir.num_points; - const double exp_value[3] = {0.9890644233322864, 0.0, 0.0}; - for (int qp = 0; qp < num_point; ++qp) - { - const auto lp = test_fixture.getTestFieldData( - eval->_exact_lagrange_pressure); - EXPECT_EQ(0.0, fieldValue(lp, 0, qp)); - - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto value = test_fixture.getTestFieldData( - eval->_exact_velocity[dim]); - EXPECT_EQ(exp_value[dim], fieldValue(value, 0, qp)); - } - - const auto ep - = test_fixture.getTestFieldData(eval->_exact_elec_pot); - EXPECT_EQ(0.0, fieldValue(ep, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(HartmannProblemExact2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(HartmannProblemExact2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(HartmannProblemExact3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(HartmannProblemExact3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("Build Temperature Equation", false); - Teuchos::Array ext_magn_vct(3); - test_fixture.user_params.set("External Magnetic Field", ext_magn_vct); - test_fixture.user_params.set("Hartmann Solution Half-Width Channel", 2.5); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.5); - test_fixture.type_name = "HartmannProblemExact"; - test_fixture.eval_name = "Exact Solution Hartmann Problem"; - test_fixture.template buildAndTest< - ClosureModel::HartmannProblemExact, - num_space_dim>(); -} - -TEST(HartmannProblemExact_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(HartmannProblemExact_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/induction_less_mhd_solver/closure_models/unit_test/tstLorentzForce.cpp b/src/induction_less_mhd_solver/closure_models/unit_test/tstLorentzForce.cpp deleted file mode 100644 index bddd2bb..0000000 --- a/src/induction_less_mhd_solver/closure_models/unit_test/tstLorentzForce.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "induction_less_mhd_solver/closure_models/VertexCFD_Closure_LorentzForce.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ - -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - int num_grad_dim; - - // quiet_NaN is a host-side function so we store the value - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField velocity_0; - PHX::MDField velocity_1; - PHX::MDField velocity_2; - PHX::MDField - grad_electric_potential; - PHX::MDField ext_magn_field_0; - PHX::MDField ext_magn_field_1; - PHX::MDField ext_magn_field_2; - - Dependencies(const panzer::IntegrationRule& ir) - : num_grad_dim(ir.spatial_dimension) - , velocity_0("velocity_0", ir.dl_scalar) - , velocity_1("velocity_1", ir.dl_scalar) - , velocity_2("velocity_2", ir.dl_scalar) - , grad_electric_potential("GRAD_electric_potential", ir.dl_vector) - , ext_magn_field_0("external_magnetic_field_0", ir.dl_scalar) - , ext_magn_field_1("external_magnetic_field_1", ir.dl_scalar) - , ext_magn_field_2("external_magnetic_field_2", ir.dl_scalar) - { - this->addEvaluatedField(velocity_0); - this->addEvaluatedField(velocity_1); - this->addEvaluatedField(velocity_2); - this->addEvaluatedField(grad_electric_potential); - this->addEvaluatedField(ext_magn_field_0); - this->addEvaluatedField(ext_magn_field_1); - this->addEvaluatedField(ext_magn_field_2); - this->setName( - "Electric Potential Lorentz Force Unit Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "electric potential lorentz force test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = velocity_0.extent(1); - for (int qp = 0; qp < num_point; ++qp) - { - velocity_0(c, qp) = 0.1; - velocity_1(c, qp) = -0.2; - grad_electric_potential(c, qp, 0) = 0.6; - grad_electric_potential(c, qp, 1) = 0.4; - if (num_grad_dim == 3) - { - velocity_2(c, qp) = 0.3; - grad_electric_potential(c, qp, 2) = 0.5; - } - else - { - velocity_2(c, qp) = _nanval; - } - ext_magn_field_0(c, qp) = 1.1; - ext_magn_field_1(c, qp) = 2.0; - ext_magn_field_2(c, qp) = -0.3; - } - } -}; - -template -void testEval() -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Initialize dependents - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize class object to test - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.375); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - fluid_prop_list.set("Build Inductionless MHD Equation", true); - fluid_prop_list.set("Electrical conductivity", 3.0); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - const auto eval = Teuchos::rcp( - new ClosureModel::LorentzForce( - ir, fluid_prop)); - - // Register - test_fixture.registerEvaluator(eval); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(eval->_lorentz_force[dim]); - - test_fixture.evaluate(); - - const int num_point = ir.num_points; - - // Assert values - double exp_values[num_space_dim]; - if (num_space_dim == 2) - { - exp_values[0] = -2.16; - exp_values[1] = 0.846; - } - else - { - exp_values[0] = 0.516; - exp_values[1] = -1.29; - exp_values[2] = -6.708; - } - - for (int qp = 0; qp < num_point; ++qp) - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto Fldim = test_fixture.getTestFieldData( - eval->_lorentz_force[dim]); - EXPECT_NEAR(exp_values[dim], fieldValue(Fldim, 0, qp), 1.0e-14); - } -} - -//-----------------------------------------------------------------// -TEST(LorentzForce2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(LorentzForce2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(LorentzForce3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(LorentzForce3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -template -void testFactory() -{ - constexpr int num_space_dim = NumSpaceDim; - ClosureModelFactoryTestFixture test_fixture; - test_fixture.user_params.set("Build Inductionless MHD Equation", true); - test_fixture.user_params.set("Build Temperature Equation", false); - test_fixture.user_params.sublist("Fluid Properties") - .set("Kinematic viscosity", 0.1) - .set("Artificial compressibility", 2.0) - .set("Electrical conductivity", 3.0); - test_fixture.type_name = "LorentzForce"; - test_fixture.eval_name = "Electric Potential Lorentz Force " - + std::to_string(num_space_dim) + "D"; - test_fixture.template buildAndTest< - ClosureModel::LorentzForce, - num_space_dim>(); -} - -TEST(LorentzForce_Factory2D, residual_test) -{ - testFactory(); -} - -TEST(LorentzForce_Factory2D, jacobian_test) -{ - testFactory(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/initial_conditions/VertexCFD_InitialConditionFactory.cpp b/src/initial_conditions/VertexCFD_InitialConditionFactory.cpp deleted file mode 100644 index 5c66f7d..0000000 --- a/src/initial_conditions/VertexCFD_InitialConditionFactory.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialConditionFactory.hpp" -#include "VertexCFD_InitialConditionFactory_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM( - VertexCFD::InitialCondition::Factory) diff --git a/src/initial_conditions/VertexCFD_InitialConditionFactory.hpp b/src/initial_conditions/VertexCFD_InitialConditionFactory.hpp deleted file mode 100644 index e09ef5b..0000000 --- a/src/initial_conditions/VertexCFD_InitialConditionFactory.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITIONFACTORY_HPP -#define VERTEXCFD_INITIALCONDITIONFACTORY_HPP - -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class Factory : public panzer::ClosureModelFactory -{ - public: - Factory(Teuchos::RCP mesh); - - Teuchos::RCP>>> - buildClosureModels(const std::string& block_id, - const Teuchos::ParameterList& block_params, - const panzer::FieldLayoutLibrary& fl, - const Teuchos::RCP& ir, - const Teuchos::ParameterList& default_params, - const Teuchos::ParameterList& user_data, - const Teuchos::RCP& global_data, - PHX::FieldManager& fm) const override; - - private: - Teuchos::RCP _mesh; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITIONFACTORY_HPP diff --git a/src/initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp b/src/initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp deleted file mode 100644 index b9d22f7..0000000 --- a/src/initial_conditions/VertexCFD_InitialConditionFactory_TemplateBuilder.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITIONFACTORY_TEMPLATEBUILDER_HPP -#define VERTEXCFD_INITIALCONDITIONFACTORY_TEMPLATEBUILDER_HPP - -#include "VertexCFD_InitialConditionFactory.hpp" - -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class FactoryTemplateBuilder -{ - public: - FactoryTemplateBuilder(Teuchos::RCP mesh) - : _mesh{mesh} - { - } - - template - Teuchos::RCP build() const - { - auto ic_factory = Teuchos::rcp(new Factory(_mesh)); - return Teuchos::rcp_static_cast( - ic_factory); - } - - private: - Teuchos::RCP _mesh; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITIONFACTORY_TEMPLATEBUILDER_HPP diff --git a/src/initial_conditions/VertexCFD_InitialConditionFactory_impl.hpp b/src/initial_conditions/VertexCFD_InitialConditionFactory_impl.hpp deleted file mode 100644 index a79eb2a..0000000 --- a/src/initial_conditions/VertexCFD_InitialConditionFactory_impl.hpp +++ /dev/null @@ -1,276 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITIONFACTORY_IMPL_HPP -#define VERTEXCFD_INITIALCONDITIONFACTORY_IMPL_HPP - -#include "VertexCFD_InitialCondition_Circle.hpp" -#include "VertexCFD_InitialCondition_Constant.hpp" -#include "VertexCFD_InitialCondition_Gaussian.hpp" -#include "VertexCFD_InitialCondition_InverseGaussian.hpp" -#include "VertexCFD_InitialCondition_MethodManufacturedSolution.hpp" -#include "VertexCFD_InitialCondition_Step.hpp" -#include "full_induction_mhd_solver/initial_conditions/VertexCFD_FullInductionInitialConditionFactory.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleLaminarFlow.hpp" -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleTaylorGreenVortex.hpp" -#include "incompressible_solver/initial_conditions/VertexCFD_InitialCondition_IncompressibleVortexInBox.hpp" - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -Factory::Factory( - Teuchos::RCP mesh) - : _mesh{mesh} -{ -} - -//---------------------------------------------------------------------------// -template -Teuchos::RCP>>> -Factory::buildClosureModels( - const std::string& block_id, - const Teuchos::ParameterList& block_params, - const panzer::FieldLayoutLibrary& fl, - const Teuchos::RCP&, - const Teuchos::ParameterList&, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP&, - PHX::FieldManager&) const -{ - auto evaluators = Teuchos::rcp( - new std::vector>>); - - // Space dimension - constexpr int num_dim_space = NumSpaceDim; - - if (!block_params.isSublist(block_id)) - { - throw std::runtime_error("Initial condition block id not in list"); - } - - // Initial conditions apply to the bases. - std::vector> bases; - fl.uniqueBases(bases); - - // Fluid properties. - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params( - fluid_prop_list); - - // Full induction solver factory objects - FullInductionICFactory full_induction_factory; - std::string full_ind_error_msg = "None"; - - // Loop over initial conditions - const Teuchos::ParameterList& ic_params = block_params.sublist(block_id); - for (auto param_itr = ic_params.begin(); param_itr != ic_params.end(); - ++param_itr) - { - bool found_model = false; - - auto key = param_itr->first; - const auto& p - = Teuchos::getValue(param_itr->second); - - if (p.isType("Type")) - { - std::string type = p.get("Type"); - - // Read initial conditions from corresponding fields of the input - // mesh file. - if (type == "From File") - { - std::vector field_names; - panzer::StringTokenizer( - field_names, p.get("Field Names"), ",", true); - - // Assume we use the same basis for all DoFs. - auto basis = fl.lookupLayout(field_names.at(0)); - - auto plist - = Teuchos::ParameterList{} - .set("Field Names", Teuchos::rcpFromRef(field_names)) - .set("Basis", basis); - - auto eval = Teuchos::rcp( - new panzer_stk::GatherFields( - _mesh, plist)); - evaluators->push_back(eval); - found_model = true; - } - - if (user_params.isSublist("Full Induction MHD Properties")) - { - full_induction_factory.buildClosureModel(type, - bases, - user_params, - p, - found_model, - full_ind_error_msg, - evaluators); - } - - if (type == "Constant") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new Constant(p, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "Gaussian") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new Gaussian( - p, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "Step") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new Step(p, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "Circle") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new Circle( - p, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "InverseGaussian") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new InverseGaussian( - p, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "MethodManufacturedSolution") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new MethodManufacturedSolution(*b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "IncompressibleVortexInBox") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new IncompressibleVortexInBox( - *b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "IncompressibleTaylorGreenVortex") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new IncompressibleTaylorGreenVortex(*b)); - evaluators->push_back(eval); - found_model = true; - } - } - - if (type == "IncompressibleLaminarFlow") - { - for (const auto& b : bases) - { - auto eval = Teuchos::rcp( - new IncompressibleLaminarFlow( - p, incompressible_fluidprop_params, *b)); - evaluators->push_back(eval); - found_model = true; - } - } - } - - if (!found_model) - { - std::string msg = "Initial condition " + key - + " failed to build.\n"; - msg += "The initial conditions implemented in VertexCFD are:\n"; - msg += "Circle\n"; - msg += "Constant\n"; - msg += "From File\n"; - msg += "Gaussian\n"; - msg += "IncompressibleLaminarFlow\n"; - msg += "IncompressibleTaylorGreenVortex\n"; - msg += "IncompressibleVortexInBox\n"; - msg += "InverseGaussian\n"; - msg += "MethodManufacturedSolution\n"; - msg += "Step\n"; - msg += "=================================\n"; - msg += "Full induction MHD closure models:\n"; - msg += full_ind_error_msg; - throw std::runtime_error(msg); - } - } - - return evaluators; -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITIONFACTORY_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Circle.cpp b/src/initial_conditions/VertexCFD_InitialCondition_Circle.cpp deleted file mode 100644 index 482e379..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Circle.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_Circle.hpp" -#include "VertexCFD_InitialCondition_Circle_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::Circle) diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Circle.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Circle.hpp deleted file mode 100644 index 70a5d03..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Circle.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_CIRCLE_HPP -#define VERTEXCFD_INITIALCONDITION_CIRCLE_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class Circle : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - - static constexpr int num_space_dim = NumSpaceDim; - - Circle(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _ic; - - private: - std::string _basis_name; - Kokkos::Array _origin; - double _inside_value, _outside_value, _radius; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_CIRCLE_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Circle_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Circle_impl.hpp deleted file mode 100644 index b1a9dcb..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Circle_impl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_CIRCLE_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_CIRCLE_IMPL_HPP - -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -Circle::Circle( - const Teuchos::ParameterList& params, const panzer::PureBasis& basis) - : _basis_name(basis.name()) -{ - _inside_value = params.get("Inside Value"); - _outside_value = params.get("Outside Value"); - auto origin = params.get>("Center"); - for (int dim = 0; dim < num_space_dim; ++dim) - { - _origin[dim] = origin[dim]; - } - _radius = params.get("Radius"); - std::string dof_name = params.get("Equation Set Name"); - _ic = PHX::MDField( - dof_name, basis.functional); - this->addEvaluatedField(_ic); - this->addUnsharedField(_ic.fieldTag().clone()); - this->setName("Circle Initial Condition: " + dof_name); -} - -//---------------------------------------------------------------------------// -template -void Circle::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void Circle::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void Circle::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _ic.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - using std::sqrt; - double sum = 0.0; - for (int dim = 0; dim < num_space_dim; ++dim) - { - const double coord = _basis_coords(cell, basis, dim) - - _origin[dim]; - sum += coord * coord; - } - const double r = sqrt(sum); - _ic(cell, basis) = r < _radius ? _inside_value : _outside_value; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_CIRCLE_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Constant.cpp b/src/initial_conditions/VertexCFD_InitialCondition_Constant.cpp deleted file mode 100644 index c523c2e..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Constant.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_Constant.hpp" -#include "VertexCFD_InitialCondition_Constant_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::InitialCondition::Constant) -#include "VertexCFD_InitialCondition_Constant_impl.hpp" diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Constant.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Constant.hpp deleted file mode 100644 index 891d8a1..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Constant.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_CONSTANT_HPP -#define VERTEXCFD_INITIALCONDITION_CONSTANT_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class Constant : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived - -{ - public: - using scalar_type = typename EvalType::ScalarT; - - Constant(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - public: - PHX::MDField _ic; - - private: - scalar_type _value; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_CONSTANT_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Constant_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Constant_impl.hpp deleted file mode 100644 index 836a934..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Constant_impl.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_CONSTANT_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_CONSTANT_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -Constant::Constant(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis) -{ - _value = params.get("Value"); - std::string dof_name = params.get("Equation Set Name"); - _ic = PHX::MDField( - dof_name, basis.functional); - this->addEvaluatedField(_ic); - this->addUnsharedField(_ic.fieldTag().clone()); - this->setName("Constant Initial Condition: " + dof_name); -} - -//---------------------------------------------------------------------------// -template -void Constant::postRegistrationSetup( - typename Traits::SetupData, PHX::FieldManager& fm) -{ - this->utils.setFieldData(_ic, fm); - _ic.deep_copy(_value); -} - -//---------------------------------------------------------------------------// -template -void Constant::evaluateFields(typename Traits::EvalData) -{ -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_CONSTANT_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.cpp b/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.cpp deleted file mode 100644 index 1e4b767..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_Gaussian.hpp" -#include "VertexCFD_InitialCondition_Gaussian_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::Gaussian) diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp deleted file mode 100644 index 5eef67a..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_GAUSSIAN_HPP -#define VERTEXCFD_INITIALCONDITION_GAUSSIAN_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class Gaussian : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - static constexpr int num_space_dim = NumSpaceDim; - - Gaussian(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _ic; - - private: - std::string _basis_name; - Kokkos::View _a; - Kokkos::View _b; - Kokkos::View _c; - double _d; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_GAUSSIAN_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Gaussian_impl.hpp deleted file mode 100644 index cde1819..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Gaussian_impl.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_GAUSSIAN_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_GAUSSIAN_IMPL_HPP - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -Gaussian::Gaussian( - const Teuchos::ParameterList& params, const panzer::PureBasis& basis) - : _basis_name(basis.name()) - , _a(Kokkos::ViewAllocateWithoutInitializing("Gaussian a"), - basis.dimension()) - , _b(Kokkos::ViewAllocateWithoutInitializing("Gaussian b"), - basis.dimension()) - , _c(Kokkos::ViewAllocateWithoutInitializing("Gaussian c"), - basis.dimension()) -{ - std::string dof_name = params.get("Equation Set Name"); - _ic = PHX::MDField( - dof_name, basis.functional); - this->addEvaluatedField(_ic); - this->addUnsharedField(_ic.fieldTag().clone()); - this->setName("Gaussian Initial Condition: " + dof_name); - - auto center = params.get>("Center"); - auto sigma = params.get>("Sigma"); - _d = params.get("Base"); - const double sqrt2pi = std::sqrt(2.0 * Constants::pi); - auto a_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _a); - auto b_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _b); - auto c_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _c); - for (int dim = 0; dim < basis.dimension(); ++dim) - { - a_host(dim) = 1.0 / (sqrt2pi * sigma[dim]); - b_host(dim) = center[dim]; - c_host(dim) = 0.5 / (sigma[dim] * sigma[dim]); - } - Kokkos::deep_copy(_a, a_host); - Kokkos::deep_copy(_b, b_host); - Kokkos::deep_copy(_c, c_host); -} - -//---------------------------------------------------------------------------// -template -void Gaussian::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void Gaussian::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void Gaussian::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _ic.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - using std::exp; - double result = 1.0; - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto x = _basis_coords(cell, basis, dim); - result *= _a(dim) - * exp((_b(dim) - x) * (x - _b(dim)) * _c(dim)); - } - _ic(cell, basis) = result + _d; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_GAUSSIAN_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.cpp b/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.cpp deleted file mode 100644 index f6377ce..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_InverseGaussian.hpp" -#include "VertexCFD_InitialCondition_InverseGaussian_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::InverseGaussian) diff --git a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp b/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp deleted file mode 100644 index aa6f3f8..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_HPP -#define VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class InverseGaussian : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - static constexpr int num_space_dim = NumSpaceDim; - - InverseGaussian(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _ic; - - private: - std::string _basis_name; - Kokkos::View _a; - Kokkos::View _b; - Kokkos::View _c; - double _d; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian_impl.hpp deleted file mode 100644 index 43be0f4..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_InverseGaussian_impl.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_IMPL_HPP - -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -InverseGaussian::InverseGaussian( - const Teuchos::ParameterList& params, const panzer::PureBasis& basis) - : _basis_name(basis.name()) - , _a(Kokkos::ViewAllocateWithoutInitializing("InverseGaussian a"), - basis.dimension()) - , _b(Kokkos::ViewAllocateWithoutInitializing("InverseGaussian b"), - basis.dimension()) - , _c(Kokkos::ViewAllocateWithoutInitializing("InverseGaussian c"), - basis.dimension()) -{ - std::string dof_name = params.get("Equation Set Name"); - _ic = PHX::MDField( - dof_name, basis.functional); - this->addEvaluatedField(_ic); - this->addUnsharedField(_ic.fieldTag().clone()); - this->setName("InverseGaussian Initial Condition: " + dof_name); - - auto center = params.get>("Center"); - auto sigma = params.get>("Sigma"); - _d = params.get("Base"); - const double sqrt2pi = std::sqrt(2.0 * Constants::pi); - auto a_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _a); - auto b_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _b); - auto c_host = Kokkos::create_mirror_view(Kokkos::HostSpace{}, _c); - for (int dim = 0; dim < basis.dimension(); ++dim) - { - a_host(dim) = 1.0 / (sqrt2pi * sigma[dim]); - b_host(dim) = center[dim]; - c_host(dim) = 0.5 / (sigma[dim] * sigma[dim]); - } - Kokkos::deep_copy(_a, a_host); - Kokkos::deep_copy(_b, b_host); - Kokkos::deep_copy(_c, c_host); -} - -//---------------------------------------------------------------------------// -template -void InverseGaussian::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void InverseGaussian::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void InverseGaussian::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _ic.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - using std::exp; - double result = 1.0; - for (int dim = 0; dim < num_space_dim; ++dim) - { - const auto x = _basis_coords(cell, basis, dim); - result *= _a(dim) - * exp((_b(dim) - x) * (x - _b(dim)) * _c(dim)); - } - _ic(cell, basis) = 1.0 / (result + _d); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_INVERSEGAUSSIAN_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.cpp b/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.cpp deleted file mode 100644 index 8f17fc7..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_MethodManufacturedSolution.hpp" -#include "VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::InitialCondition::MethodManufacturedSolution) diff --git a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp b/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp deleted file mode 100644 index eb4b89c..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_HPP -#define VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -template -class MethodManufacturedSolution - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - static constexpr int num_coeff = 2 * (num_space_dim + 1); - - MethodManufacturedSolution(const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - PHX::MDField _lagrange_pressure; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField _temperature; - - private: - std::string _basis_name; - int _basis_index; - PHX::MDField _basis_coords; - - Kokkos::Array _phi_coeff; - Kokkos::Array, num_space_dim> _vel_coeff; - Kokkos::Array _T_coeff; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#include "VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp" - -#endif // end VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp deleted file mode 100644 index 1c91b3c..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution_impl.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_IMPL_HPP - -#include "utils/VertexCFD_Utils_Constants.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include -#include -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -MethodManufacturedSolution::MethodManufacturedSolution( - const panzer::PureBasis& basis) - : _lagrange_pressure("lagrange_pressure", basis.functional) - , _temperature("temperature", basis.functional) - , _basis_name(basis.name()) -{ - this->addEvaluatedField(_lagrange_pressure); - this->addUnsharedField(_lagrange_pressure.fieldTag().clone()); - this->addEvaluatedField(_temperature); - this->addUnsharedField(_temperature.fieldTag().clone()); - - Utils::addEvaluatedVectorField( - *this, basis.functional, _velocity, "velocity_", true); - - this->setName("MethodManufacturedSolution Initial Condition" - + std::to_string(num_space_dim) + "D"); - - _phi_coeff[0] = 0.0125; - _phi_coeff[1] = 1.0; - _phi_coeff[2] = 0.25; - _phi_coeff[3] = 0.5; - _phi_coeff[4] = 0.125; - _phi_coeff[5] = 0.0; - - _vel_coeff[0][0] = 0.0125; - _vel_coeff[0][1] = 0.08; - _vel_coeff[0][2] = 0.125; - _vel_coeff[0][3] = 0.0; - _vel_coeff[0][4] = 0.125; - _vel_coeff[0][5] = 0.25; - - _vel_coeff[1][0] = 0.0375; - _vel_coeff[1][1] = 1.125; - _vel_coeff[1][2] = 0.25; - _vel_coeff[1][3] = 0.0; - _vel_coeff[1][4] = 0.375; - _vel_coeff[1][5] = 0.5; - - _T_coeff[0] = 0.0625; - _T_coeff[1] = 1.0; - _T_coeff[2] = 0.375; - _T_coeff[3] = 0.25; - _T_coeff[4] = 0.25; - _T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - _phi_coeff[6] = 0.375; - _phi_coeff[7] = 1.0; - - _vel_coeff[0][6] = 0.25; - _vel_coeff[0][7] = 0.0; - - _vel_coeff[1][6] = 0.25; - _vel_coeff[1][7] = 0.5; - - _vel_coeff[2][0] = 0.025; - _vel_coeff[2][1] = 0.0; - _vel_coeff[2][2] = 0.125; - _vel_coeff[2][3] = 1.0; - _vel_coeff[2][4] = 0.25; - _vel_coeff[2][5] = 0.25; - _vel_coeff[2][6] = 0.25; - _vel_coeff[2][7] = 0.25; - - _T_coeff[6] = 0.125; - _T_coeff[7] = 0.5; - } -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::postRegistrationSetup( - typename Traits::SetupData sd, PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::evaluateFields( - typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void MethodManufacturedSolution::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _lagrange_pressure.extent(1); - using Constants::pi; - using std::sin; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - // function = B + A * sin(2*pi*f_x*(x-phi_x)) * - // sin(2*pi*f_y*(x-phi_y)) - // * sin(2*pi*f_z*(z-phi_z)) for 3D - auto set_function - = [=](const Kokkos::Array coeff, - const Kokkos::Array x) { - double return_val = coeff[0]; - for (int i = 0; i < num_space_dim; ++i) - return_val *= sin(2.0 * pi * coeff[2 * (i + 1)] - * (x[i] - coeff[2 * (i + 1) + 1])); - return_val += coeff[1]; - return return_val; - }; - - // Note this block is all of `auto` type rather than `scalar_type` - // because the values coming from `_basis_coords` are never going - // to be FAD objects. By using `auto` instead of `double` we don't - // impose a precision on the mesh positions. - - // FIXME: Kokkos::Array is okay?? - Kokkos::Array x; - for (int dim = 0; dim < num_space_dim; ++dim) - x[dim] = _basis_coords(cell, basis, dim); - - _lagrange_pressure(cell, basis) = set_function(_phi_coeff, x); - _temperature(cell, basis) = set_function(_T_coeff, x); - for (int i = 0; i < num_space_dim; ++i) - _velocity[i](cell, basis) = set_function(_vel_coeff[i], x); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_METHODMANUFACTUREDSOLUTION_IMPL_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Step.cpp b/src/initial_conditions/VertexCFD_InitialCondition_Step.cpp deleted file mode 100644 index 7cbd51e..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Step.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_InitialCondition_Step.hpp" -#include "VertexCFD_InitialCondition_Step_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::InitialCondition::Step) diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Step.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Step.hpp deleted file mode 100644 index d057106..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Step.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_STEP_HPP -#define VERTEXCFD_INITIALCONDITION_STEP_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -class Step : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - using view_layout = typename PHX::DevLayout::type; - - Step(const Teuchos::ParameterList& params, const panzer::PureBasis& basis); - - void postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager& fm) override; - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _ic; - - private: - std::string _basis_name; - scalar_type _left_value, _right_value, _origin; - int _basis_index; - PHX::MDField _basis_coords; -}; - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_STEP_HPP diff --git a/src/initial_conditions/VertexCFD_InitialCondition_Step_impl.hpp b/src/initial_conditions/VertexCFD_InitialCondition_Step_impl.hpp deleted file mode 100644 index 7f3df6a..0000000 --- a/src/initial_conditions/VertexCFD_InitialCondition_Step_impl.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_INITIALCONDITION_STEP_IMPL_HPP -#define VERTEXCFD_INITIALCONDITION_STEP_IMPL_HPP - -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace InitialCondition -{ -//---------------------------------------------------------------------------// -template -Step::Step(const Teuchos::ParameterList& params, - const panzer::PureBasis& basis) - : _basis_name(basis.name()) -{ - _left_value = params.get("Left Value"); - _right_value = params.get("Right Value"); - _origin = params.get("Origin"); - std::string dof_name = params.get("Equation Set Name"); - _ic = PHX::MDField( - dof_name, basis.functional); - this->addEvaluatedField(_ic); - this->addUnsharedField(_ic.fieldTag().clone()); - this->setName("Step Initial Condition: " + dof_name); -} - -//---------------------------------------------------------------------------// -template -void Step::postRegistrationSetup(typename Traits::SetupData sd, - PHX::FieldManager&) -{ - _basis_index = panzer::getPureBasisIndex( - _basis_name, (*sd.worksets_)[0], this->wda); -} - -//---------------------------------------------------------------------------// -template -void Step::evaluateFields(typename Traits::EvalData workset) -{ - _basis_coords = this->wda(workset).bases[_basis_index]->basis_coordinates; - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void Step::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_basis = _ic.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_basis), [&](const int basis) { - const double x = _basis_coords(cell, basis, 0); - _ic(cell, basis) = x < _origin ? _left_value : _right_value; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace InitialCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_INITIALCONDITION_STEP_IMPL_HPP diff --git a/src/initial_conditions/unit_test/CMakeLists.txt b/src/initial_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index 30e4de0..0000000 --- a/src/initial_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - InitialConditionCircle - InitialConditionConstant - InitialConditionGaussian - InitialConditionStep - MethodManufacturedSolutionIC - ) diff --git a/src/initial_conditions/unit_test/doc/gaussian_reference.py b/src/initial_conditions/unit_test/doc/gaussian_reference.py deleted file mode 100644 index a531806..0000000 --- a/src/initial_conditions/unit_test/doc/gaussian_reference.py +++ /dev/null @@ -1,122 +0,0 @@ -from decimal import * -import math - -places = Decimal(10)**-16 - - -def show(d): - print(d.quantize(places)) - - -zero = Decimal(0) -half = Decimal(0.5) -one = Decimal(1) -two = Decimal(2) -three = Decimal(3) -twopi = two * Decimal(math.pi) -third = one / three -twothird = two * third - -center0 = third -center1 = half -center2 = twothird - -sigma0 = half -sigma1 = two -sigma2 = three - -base = third - -a0 = one / (twopi.sqrt() * sigma0) -a1 = one / (twopi.sqrt() * sigma1) -a2 = one / (twopi.sqrt() * sigma2) - -b0 = center0 -b1 = center1 -b2 = center2 - -c0 = half / (sigma0 * sigma0) -c1 = half / (sigma1 * sigma1) -c2 = half / (sigma2 * sigma2) - -r0 = zero -r1 = one -r2 = one - - -# Gaussian -def dimResult(a, b, c, x): - return a * Decimal(math.exp((b - x) * (x - b) * c)) - - -# one-dimensional mesh -print("1D case:") -print("Gaussian") -result0 = dimResult(a0, b0, c0, r0) + base -result1 = dimResult(a0, b0, c0, r1) + base - -show(result0) -show(result1) - -print("Inverse Gaussian") -show(one / result0) -show(one / result1) - -# two-dimensional mesh -print("\n2D case:") -print("Gaussian") -result0 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r0) + base -result1 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r0) + base -result2 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r1) + base -result3 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r1) + base - -show(result0) -show(result1) -show(result2) -show(result3) - -# Inverse Gaussian -print("Inverse Gaussian") -show(one / result0) -show(one / result1) -show(one / result2) -show(one / result3) - -# three-dimensional mesh -print("\n3D case:") -print("Gaussian") -result0 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r0) * dimResult( - a2, b2, c2, r0) + base -result1 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r0) * dimResult( - a2, b2, c2, r0) + base -result2 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r1) * dimResult( - a2, b2, c2, r0) + base -result3 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r1) * dimResult( - a2, b2, c2, r0) + base -result4 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r0) * dimResult( - a2, b2, c2, r2) + base -result5 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r0) * dimResult( - a2, b2, c2, r2) + base -result6 = dimResult(a0, b0, c0, r1) * dimResult(a1, b1, c1, r1) * dimResult( - a2, b2, c2, r1) + base -result7 = dimResult(a0, b0, c0, r0) * dimResult(a1, b1, c1, r1) * dimResult( - a2, b2, c2, r1) + base - -show(result0) -show(result1) -show(result2) -show(result3) -show(result4) -show(result5) -show(result6) -show(result7) - -print("Inverse Gaussian") -show(one / result0) -show(one / result1) -show(one / result2) -show(one / result3) -show(one / result4) -show(one / result5) -show(one / result6) -show(one / result7) diff --git a/src/initial_conditions/unit_test/tstInitialConditionCircle.cpp b/src/initial_conditions/unit_test/tstInitialConditionCircle.cpp deleted file mode 100644 index 0ce8ff7..0000000 --- a/src/initial_conditions/unit_test/tstInitialConditionCircle.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "initial_conditions/VertexCFD_InitialCondition_Circle.hpp" - -#include -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - const int integration_order = 1; - const int basis_order = 1; - constexpr int num_space_dim = NumSpaceDim; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - Teuchos::ParameterList params; - const double inside_value = 1.3; - const double outside_value = 2.5; - Teuchos::Array center(num_space_dim); - center[0] = 0.9; - center[1] = 0.92; - if (num_space_dim > 2) - center[2] = 0.95; - - const double radius = 0.2; - params.set("Inside Value", inside_value); - params.set("Outside Value", outside_value); - params.set>("Center", center); - params.set("Radius", radius); - params.set("Equation Set Name", "dof"); - auto eval = Teuchos::rcp( - new InitialCondition::Circle( - params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_ic); - - test_fixture.evaluate(); - - auto ic = test_fixture.getTestFieldData(eval->_ic); - - // Number of degree of freedom based on `num_space_dim` value - int num_dofs = 4; - if (num_space_dim == 3) - num_dofs = 8; - EXPECT_EQ(num_dofs, ic.extent(1)); - - // Check on values - if (num_space_dim == 2) - { - EXPECT_EQ(outside_value, fieldValue(ic, 0, 0)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 1)); - EXPECT_EQ(inside_value, fieldValue(ic, 0, 2)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 3)); - } - else if (num_space_dim == 3) - { - EXPECT_EQ(outside_value, fieldValue(ic, 0, 0)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 1)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 2)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 3)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 4)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 5)); - EXPECT_EQ(inside_value, fieldValue(ic, 0, 6)); - EXPECT_EQ(outside_value, fieldValue(ic, 0, 7)); - } -} - -//---------------------------------------------------------------------------// -TEST(Circle2D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Circle2D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Circle3D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Circle3D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/initial_conditions/unit_test/tstInitialConditionConstant.cpp b/src/initial_conditions/unit_test/tstInitialConditionConstant.cpp deleted file mode 100644 index e679881..0000000 --- a/src/initial_conditions/unit_test/tstInitialConditionConstant.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "initial_conditions/VertexCFD_InitialCondition_Constant.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - Teuchos::ParameterList params; - const double ic_value = 1.3; - params.set("Value", ic_value); - params.set("Equation Set Name", "dof"); - auto eval = Teuchos::rcp( - new InitialCondition::Constant( - params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_ic); - - test_fixture.evaluate(); - - auto ic = test_fixture.getTestFieldData(eval->_ic); - const int num_point = ic.extent(1); - - // Number of degree of freedom - int num_dofs = 4; - EXPECT_EQ(num_dofs, num_point); - - for (int b = 0; b < num_point; ++b) - { - EXPECT_EQ(ic_value, fieldValue(ic, 0, b)); - } -} - -//---------------------------------------------------------------------------// -TEST(Constant, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Constant, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/initial_conditions/unit_test/tstInitialConditionGaussian.cpp b/src/initial_conditions/unit_test/tstInitialConditionGaussian.cpp deleted file mode 100644 index 65a9cfa..0000000 --- a/src/initial_conditions/unit_test/tstInitialConditionGaussian.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "initial_conditions/VertexCFD_InitialCondition_Gaussian.hpp" -#include "initial_conditions/VertexCFD_InitialCondition_InverseGaussian.hpp" - -#include -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testGaussian() -{ - const int integration_order = 1; - const int basis_order = 1; - constexpr int num_space_dim = NumSpaceDim; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - Teuchos::ParameterList params; - Teuchos::Array center(num_space_dim); - center[0] = 1.0 / 3.0; - center[1] = 0.5; - if (num_space_dim > 2) - center[2] = 2.0 / 3.0; - params.set>("Center", center); - Teuchos::Array sigma(num_space_dim); - sigma[0] = 0.5; - sigma[1] = 2.0; - if (num_space_dim > 2) - sigma[2] = 3.0; - params.set>("Sigma", sigma); - const double base = 1.0 / 3.0; - params.set("Base", base); - params.set("Equation Set Name", "dof"); - auto eval = Teuchos::rcp( - new InitialCondition::Gaussian( - params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_ic); - - test_fixture.evaluate(); - - auto ic = test_fixture.getTestFieldData(eval->_ic); - - // Number of degree of freedom based on `num_space_dim` value - int num_dofs = 4; - if (num_space_dim == 3) - num_dofs = 8; - EXPECT_EQ(num_dofs, ic.extent(1)); - - if (num_space_dim == 2) - { - EXPECT_DOUBLE_EQ(0.4568536920450875, fieldValue(ic, 0, 0)); - EXPECT_DOUBLE_EQ(0.3967508000449945, fieldValue(ic, 0, 1)); - EXPECT_DOUBLE_EQ(0.3967508000449945, fieldValue(ic, 0, 2)); - EXPECT_DOUBLE_EQ(0.4568536920450875, fieldValue(ic, 0, 3)); - } - else if (num_space_dim == 3) - { - EXPECT_DOUBLE_EQ(0.3493585546023939, fieldValue(ic, 0, 0)); - EXPECT_DOUBLE_EQ(0.3415609562691542, fieldValue(ic, 0, 1)); - EXPECT_DOUBLE_EQ(0.3415609562691542, fieldValue(ic, 0, 2)); - EXPECT_DOUBLE_EQ(0.3493585546023939, fieldValue(ic, 0, 3)); - EXPECT_DOUBLE_EQ(0.3496580828086895, fieldValue(ic, 0, 4)); - EXPECT_DOUBLE_EQ(0.3417147391778995, fieldValue(ic, 0, 5)); - EXPECT_DOUBLE_EQ(0.3417147391778995, fieldValue(ic, 0, 6)); - EXPECT_DOUBLE_EQ(0.3496580828086895, fieldValue(ic, 0, 7)); - } -} - -//---------------------------------------------------------------------------// -TEST(Gaussian2D, residual_test) -{ - testGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(Gaussian2D, jacobian_test) -{ - testGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(Gaussian3D, residual_test) -{ - testGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(Gaussian3D, jacobian_test) -{ - testGaussian(); -} - -//---------------------------------------------------------------------------// -template -void testInverseGaussian() -{ - const int integration_order = 1; - const int basis_order = 1; - constexpr int num_space_dim = NumSpaceDim; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - Teuchos::ParameterList params; - Teuchos::Array center(num_space_dim); - center[0] = 1.0 / 3.0; - center[1] = 0.5; - if (num_space_dim > 2) - center[2] = 2.0 / 3.0; - params.set>("Center", center); - Teuchos::Array sigma(num_space_dim); - sigma[0] = 0.5; - sigma[1] = 2.0; - if (num_space_dim > 2) - sigma[2] = 3.0; - params.set>("Sigma", sigma); - const double base = 1.0 / 3.0; - params.set("Base", base); - params.set("Equation Set Name", "dof"); - auto eval = Teuchos::rcp( - new InitialCondition::InverseGaussian( - params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_ic); - - test_fixture.evaluate(); - - auto ic = test_fixture.getTestFieldData(eval->_ic); - - // Number of degree of freedom based on `num_space_dim` value - int num_dofs = 4; - if (num_space_dim == 3) - num_dofs = 8; - EXPECT_EQ(num_dofs, ic.extent(1)); - - if (num_space_dim == 2) - { - EXPECT_DOUBLE_EQ(2.1888845759865475, fieldValue(ic, 0, 0)); - EXPECT_DOUBLE_EQ(2.5204738084626235, fieldValue(ic, 0, 1)); - EXPECT_DOUBLE_EQ(2.5204738084626235, fieldValue(ic, 0, 2)); - EXPECT_DOUBLE_EQ(2.1888845759865475, fieldValue(ic, 0, 3)); - } - else if (num_space_dim == 3) - { - EXPECT_DOUBLE_EQ(2.8623887602755378, fieldValue(ic, 0, 0)); - EXPECT_DOUBLE_EQ(2.9277350986568493, fieldValue(ic, 0, 1)); - EXPECT_DOUBLE_EQ(2.9277350986568493, fieldValue(ic, 0, 2)); - EXPECT_DOUBLE_EQ(2.8623887602755378, fieldValue(ic, 0, 3)); - EXPECT_DOUBLE_EQ(2.8599367472569936, fieldValue(ic, 0, 4)); - EXPECT_DOUBLE_EQ(2.9264175212512321, fieldValue(ic, 0, 5)); - EXPECT_DOUBLE_EQ(2.9264175212512321, fieldValue(ic, 0, 6)); - EXPECT_DOUBLE_EQ(2.8599367472569936, fieldValue(ic, 0, 7)); - } -} - -//---------------------------------------------------------------------------// -TEST(InverseGaussian2D, residual_test) -{ - testInverseGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(InverseGaussian2D, jacobian_test) -{ - testInverseGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(InverseGaussian3D, residual_test) -{ - testInverseGaussian(); -} - -//---------------------------------------------------------------------------// -TEST(InverseGaussian3D, jacobian_test) -{ - testInverseGaussian(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/initial_conditions/unit_test/tstInitialConditionStep.cpp b/src/initial_conditions/unit_test/tstInitialConditionStep.cpp deleted file mode 100644 index 15b4678..0000000 --- a/src/initial_conditions/unit_test/tstInitialConditionStep.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "initial_conditions/VertexCFD_InitialCondition_Step.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testEval() -{ - const int integration_order = 1; - const int basis_order = 1; - constexpr int num_space_dim = NumSpaceDim; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - Teuchos::ParameterList params; - const double left_value = 1.3; - const double right_value = 2.5; - const double origin = 0.5; - params.set("Left Value", left_value); - params.set("Right Value", right_value); - params.set("Origin", origin); - params.set("Equation Set Name", "dof"); - auto eval - = Teuchos::rcp(new InitialCondition::Step( - params, *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_ic); - - test_fixture.evaluate(); - - auto ic = test_fixture.getTestFieldData(eval->_ic); - - // Number of degree of freedom based on `num_space_dim` value - int num_dofs = 4; - if (num_space_dim == 3) - num_dofs = 8; - EXPECT_EQ(num_dofs, ic.extent(1)); - - if (num_space_dim == 2) - { - EXPECT_EQ(left_value, fieldValue(ic, 0, 0)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 1)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 2)); - EXPECT_EQ(left_value, fieldValue(ic, 0, 3)); - } - else if (num_space_dim == 3) - { - EXPECT_EQ(left_value, fieldValue(ic, 0, 0)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 1)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 2)); - EXPECT_EQ(left_value, fieldValue(ic, 0, 3)); - EXPECT_EQ(left_value, fieldValue(ic, 0, 4)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 5)); - EXPECT_EQ(right_value, fieldValue(ic, 0, 6)); - EXPECT_EQ(left_value, fieldValue(ic, 0, 7)); - } -} - -//---------------------------------------------------------------------------// -TEST(Step2D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Step2D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Step3D, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(Step3D, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/initial_conditions/unit_test/tstMethodManufacturedSolutionIC.cpp b/src/initial_conditions/unit_test/tstMethodManufacturedSolutionIC.cpp deleted file mode 100644 index aea2130..0000000 --- a/src/initial_conditions/unit_test/tstMethodManufacturedSolutionIC.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" - -#include "initial_conditions/VertexCFD_InitialCondition_MethodManufacturedSolution.hpp" -#include "utils/VertexCFD_Utils_Constants.hpp" - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------//¬ -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// function = B + A * sin(2*pi*f_x*(x-phi_x)) * -// sin(2*pi*f_y*(x-phi_y)) -// * sin(2*pi*f_z*(z-phi_z)) for 3D -// FIXME: warning: lambda templates are only available with ‘-std=c++20’ or -// ‘-std=gnu++20’ -// -template -double -set_function(const Kokkos::Array coeff, const SubviewType& x) -{ - using std::sin; - using VertexCFD::Constants::pi; - - double return_val = coeff[0]; - for (int i = 0; i < num_space_dim; ++i) - return_val *= sin(2.0 * pi * coeff[2 * (i + 1)] - * (x[i] - coeff[2 * (i + 1) + 1])); - return_val += coeff[1]; - return return_val; -} - -//---------------------------------------------------------------------------// -template -void testEval() -{ - // Test fixture - const int num_space_dim = NumSpaceDim; - const int num_coeff = 2 * (num_space_dim + 1); - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const int num_coord = test_fixture.cell_topo->getNodeCount(); - - // Set non-trivial coordinates for the degree of freedom - Kokkos::View x( - "coordinate", num_space_dim, num_coord); - auto basis_coord_view - = test_fixture.workset->bases[0]->basis_coordinates.get_static_view(); - auto basis_coord_mirror = Kokkos::create_mirror(basis_coord_view); - for (int dim = 0; dim < num_space_dim; dim++) - { - for (int basis = 0; basis < num_coord; basis++) - { - // random coordinate assigned - x(dim, basis) = (0.5 + dim * 4.5) * (basis + 1); - basis_coord_mirror(0, basis, dim) = x(dim, basis); - } - } - Kokkos::deep_copy(basis_coord_view, basis_coord_mirror); - - // Create mms evaluator. - auto mms_eval = Teuchos::rcp( - new InitialCondition:: - MethodManufacturedSolution( - *test_fixture.basis_ir_layout->getBasis())); - test_fixture.registerEvaluator(mms_eval); - - // Coefficients to be used with the above function to compute reference - // values for each primitive variable. They are hard coded in the source - // code - Kokkos::Array phi_coeff; - Kokkos::Array, num_space_dim> vel_coeff; - Kokkos::Array T_coeff; - - phi_coeff[0] = 0.0125; - phi_coeff[1] = 1.0; - phi_coeff[2] = 0.25; - phi_coeff[3] = 0.5; - phi_coeff[4] = 0.125; - phi_coeff[5] = 0.0; - - vel_coeff[0][0] = 0.0125; - vel_coeff[0][1] = 0.08; - vel_coeff[0][2] = 0.125; - vel_coeff[0][3] = 0.0; - vel_coeff[0][4] = 0.125; - vel_coeff[0][5] = 0.25; - - vel_coeff[1][0] = 0.0375; - vel_coeff[1][1] = 1.125; - vel_coeff[1][2] = 0.25; - vel_coeff[1][3] = 0.0; - vel_coeff[1][4] = 0.375; - vel_coeff[1][5] = 0.5; - - T_coeff[0] = 0.0625; - T_coeff[1] = 1.0; - T_coeff[2] = 0.375; - T_coeff[3] = 0.25; - T_coeff[4] = 0.25; - T_coeff[5] = 0.5; - - if (num_space_dim == 3) - { - phi_coeff[6] = 0.375; - phi_coeff[7] = 1.0; - - vel_coeff[0][6] = 0.25; - vel_coeff[0][7] = 0.0; - - vel_coeff[1][6] = 0.25; - vel_coeff[1][7] = 0.5; - - vel_coeff[2][0] = 0.025; - vel_coeff[2][1] = 0.0; - vel_coeff[2][2] = 0.125; - vel_coeff[2][3] = 1.0; - vel_coeff[2][4] = 0.25; - vel_coeff[2][5] = 0.25; - vel_coeff[2][6] = 0.25; - vel_coeff[2][7] = 0.25; - - T_coeff[6] = 0.125; - T_coeff[7] = 0.5; - } - - // Add required test fields. - test_fixture.registerTestField(mms_eval->_lagrange_pressure); - for (int dim = 0; dim < num_space_dim; ++dim) - test_fixture.registerTestField(mms_eval->_velocity[dim]); - test_fixture.registerTestField(mms_eval->_temperature); - - // Evaluate MMS IC. - test_fixture.evaluate(); - - // Check the IC value for all scalars. - const auto phi_result = test_fixture.getTestFieldData( - mms_eval->_lagrange_pressure); - const auto velocity_0_result - = test_fixture.getTestFieldData(mms_eval->_velocity[0]); - const auto velocity_1_result - = test_fixture.getTestFieldData(mms_eval->_velocity[1]); - const auto temperature_result - = test_fixture.getTestFieldData(mms_eval->_temperature); - - // Assert number of points - EXPECT_EQ(num_coord, phi_result.extent(1)); - EXPECT_EQ(num_coord, velocity_0_result.extent(1)); - EXPECT_EQ(num_coord, velocity_1_result.extent(1)); - EXPECT_EQ(num_coord, temperature_result.extent(1)); - - // Loop over number of points and compare against reference values - int num_point = phi_result.extent(1); - for (int d = 0; d < num_point; ++d) - { - auto x_subview = Kokkos::subview(x, Kokkos::ALL(), d); - - // Compute reference values - const double phi_ref - = set_function(phi_coeff, x_subview); - const double u_ref - = set_function(vel_coeff[0], x_subview); - const double v_ref - = set_function(vel_coeff[1], x_subview); - const double T_ref - = set_function(T_coeff, x_subview); - - // Assert - EXPECT_DOUBLE_EQ(phi_ref, fieldValue(phi_result, 0, d)); - EXPECT_DOUBLE_EQ(u_ref, fieldValue(velocity_0_result, 0, d)); - EXPECT_DOUBLE_EQ(v_ref, fieldValue(velocity_1_result, 0, d)); - EXPECT_DOUBLE_EQ(T_ref, fieldValue(temperature_result, 0, d)); - } - - if (num_space_dim == 3) - { - const auto velocity_2_result - = test_fixture.getTestFieldData(mms_eval->_velocity[2]); - EXPECT_EQ(num_coord, velocity_2_result.extent(1)); - - for (int d = 0; d < num_point; ++d) - { - auto x_subview = Kokkos::subview(x, Kokkos::ALL(), d); - const double w_ref = set_function( - vel_coeff[2], x_subview); - - // Assert - EXPECT_DOUBLE_EQ(w_ref, fieldValue(velocity_2_result, 0, d)); - } - } -} - -//---------------------------------------------------------------------------// -// Method of Manufactured Solution IC -// Residual -TEST(MethodManufacturedSolutionIC2D, residual_mms_ic_test) -{ - testEval(); -} - -// Jacobian -TEST(MethodManufacturedSolutionIC2D, jacobian_mms_ic_test) -{ - testEval(); -} - -TEST(MethodManufacturedSolutionIC3D, residual_mms_ic_test) -{ - testEval(); -} - -// Jacobian -TEST(MethodManufacturedSolutionIC3D, jacobian_mms_ic_test) -{ - testEval(); -} -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.cpp deleted file mode 100644 index ecaa8cf..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.cpp +++ /dev/null @@ -1,325 +0,0 @@ -#include "VertexCFD_LinearSolvers_CusolverGLU.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Constructor -//---------------------------------------------------------------------------// -CusolverGLU::CusolverGLU(const Teuchos::ParameterList& params) - : _matrix_set(false) - , _initialized(false) - , _computed(false) -{ - // Get factorization parameters - _pivot_threshold = 0.01; - if (params.isType("Pivot Threshold")) - _pivot_threshold = params.get("Pivot Threshold"); - - // Reorder parameter, 0 is no reordering, 1 uses METIS - _reorder = 1; - if (params.isType("Reorder")) - _reorder = params.get("Reorder"); -} - -//---------------------------------------------------------------------------// -// Change matrix -//---------------------------------------------------------------------------// -void CusolverGLU::setMatrix(Teuchos::RCP> A) -{ - _A = A; - - // Host-side allocation for CRS data - _A_rowptr_host.clear(); - _A_colind_host.clear(); - _A_values_host.clear(); - - // Extract matrix data for factorizations - int local_rows = this->num_local_rows(); - std::size_t num_entries; - std::size_t max_entries = this->max_entries_per_row(); - Tpetra::RowMatrix<>::nonconst_local_inds_host_view_type row_inds( - "row_indices", max_entries); - Tpetra::RowMatrix<>::nonconst_values_host_view_type row_vals("row_values", - max_entries); - _A_rowptr_host.push_back(0); - for (int row = 0; row < local_rows; ++row) - { - num_entries = _A->getNumEntriesInLocalRow(row); - _A->getLocalRowCopy(row, row_inds, row_vals, num_entries); - _A_colind_host.insert(_A_colind_host.end(), - row_inds.data(), - row_inds.data() + num_entries); - _A_values_host.insert(_A_values_host.end(), - row_vals.data(), - row_vals.data() + num_entries); - _A_rowptr_host.push_back(_A_rowptr_host.back() + num_entries); - } - - // Copy data to device - _A_rowptr = _A_rowptr_host; - _A_colind = _A_colind_host; - _A_values = _A_values_host; - - _matrix_set = true; - _computed = false; -} - -//---------------------------------------------------------------------------// -// Initialize preconditioner -//---------------------------------------------------------------------------// -void CusolverGLU::initialize() -{ - assert(_matrix_set); - assert(!_computed); - - if (_initialized) - { - return; - } - - // In future Trilinos versions (13.4+), it might be possible to access the - // internal matrix data on both host and device without making extra - // copies. For now, the only way to get the data is to extract the values - // row-by-row on the host and then explicitly copy to the device. - - int num_rows = this->num_local_rows(); - int A_nnz = this->num_local_entries(); - - cusolverStatus_t stat; - cusolverSpCreate(&_handle); - cusparseCreateMatDescr(&_A_descr); - cusparseSetMatType(_A_descr, CUSPARSE_MATRIX_TYPE_GENERAL); - cusparseSetMatIndexBase(_A_descr, CUSPARSE_INDEX_BASE_ZERO); - - cusolverSpCreateCsrluInfoHost(&_lu_info); - - // Allocate pivot data - std::vector P(num_rows); - std::vector Q(num_rows); - - stat = cusolverSpXcsrluConfigHost(_lu_info, _reorder); - check_status(stat, "cuSOLVER CSR config"); - - // Host-side analysis - stat = cusolverSpXcsrluAnalysisHost(_handle, - num_rows, - A_nnz, - _A_descr, - _A_rowptr_host.data(), - _A_colind_host.data(), - _lu_info); - check_status(stat, "cuSOLVER host analysis"); - - size_t internalDataInBytes; - size_t workspaceInBytes; - stat = cusolverSpDcsrluBufferInfoHost(_handle, - num_rows, - A_nnz, - _A_descr, - _A_values_host.data(), - _A_rowptr_host.data(), - _A_colind_host.data(), - _lu_info, - &internalDataInBytes, - &workspaceInBytes); - check_status(stat, "cuSOLVER buffer info"); - - std::vector h_work(workspaceInBytes); - stat = cusolverSpDcsrluFactorHost(_handle, - num_rows, - A_nnz, - _A_descr, - _A_values_host.data(), - _A_rowptr_host.data(), - _A_colind_host.data(), - _lu_info, - _pivot_threshold, - h_work.data()); - check_status(stat, "cuSOLVER host factorization"); - - // Prepare to extract M (compressed LU factors) - cusolverSpCreateGluInfo(&_M_info); - cusparseCreateMatDescr(&_M_descr); - cusparseSetMatType(_M_descr, CUSPARSE_MATRIX_TYPE_GENERAL); - cusparseSetMatIndexBase(_M_descr, CUSPARSE_INDEX_BASE_ZERO); - - int M_nnz; - stat = cusolverSpXcsrluNnzMHost(_handle, &M_nnz, _lu_info); - assert(CUSOLVER_STATUS_SUCCESS == stat); - check_status(stat, "cuSOLVER CSR nnz"); - - std::vector M_rowptr(num_rows + 1); - std::vector M_colind(M_nnz); - - stat = cusolverSpDcsrluExtractMHost(_handle, - P.data(), - Q.data(), - _M_descr, - NULL, /* csrValM */ - M_rowptr.data(), - M_colind.data(), - _lu_info, - h_work.data()); - check_status(stat, "cuSOLVER CSR extract"); - - // Set up GLU using host data - stat = cusolverSpDgluSetup(_handle, - num_rows, - A_nnz, - _A_descr, - _A_rowptr_host.data(), - _A_colind_host.data(), - P.data(), - Q.data(), - M_nnz, - _M_descr, - M_rowptr.data(), - M_colind.data(), - _M_info); - check_status(stat, "GLU setup"); - - size_t buffer_size; - stat = cusolverSpDgluBufferSize(_handle, _M_info, &buffer_size); - check_status(stat, "GLU buffer allocation"); - - _work.resize(buffer_size); - stat = cusolverSpDgluAnalysis(_handle, _M_info, _work.data().get()); - check_status(stat, "GLU analysis"); - - _initialized = true; -} - -//---------------------------------------------------------------------------// -// Compute preconditioner -//---------------------------------------------------------------------------// -void CusolverGLU::compute() -{ - assert(_matrix_set); - assert(_initialized); - assert(!_computed); - - int num_rows = this->num_local_rows(); - int A_nnz = this->num_local_entries(); - - cusolverStatus_t stat; - stat = cusolverSpDgluReset(_handle, - num_rows, - A_nnz, - _A_descr, - _A_values.data().get(), - _A_rowptr.data().get(), - _A_colind.data().get(), - _M_info); - check_status(stat, "GLU reset"); - - stat = cusolverSpDgluFactor(_handle, _M_info, _work.data().get()); - check_status(stat, "GLU refactor"); - - _computed = true; -} - -//---------------------------------------------------------------------------// -// Apply preconditioner -//---------------------------------------------------------------------------// -void CusolverGLU::solve(const Tpetra::MultiVector<>& b, - Tpetra::MultiVector<>& x) -{ - cudaDeviceSynchronize(); - - assert(_initialized); - assert(_computed); - - int num_rows = this->num_local_rows(); - int A_nnz = this->num_local_entries(); - - // Get Kokkos Views - auto b_view = b.getLocalViewDevice(Tpetra::Access::ReadOnly); - auto x_view = x.getLocalViewDevice(Tpetra::Access::OverwriteAll); - - cusolverStatus_t stat; - int ite_refine_succ = 0; - double r_nrminf; - stat = cusolverSpDgluSolve(_handle, - num_rows, - A_nnz, - _A_descr, - _A_values.data().get(), - _A_rowptr.data().get(), - _A_colind.data().get(), - b_view.data(), - x_view.data(), - &ite_refine_succ, - &r_nrminf, - _M_info, - _work.data().get()); - check_status(stat, "GLU solve"); - if (ite_refine_succ != 1) - { - std::stringstream ss; - ss << "GLU iterative refinement failed with status " << ite_refine_succ; - throw std::runtime_error(ss.str()); - } -} - -//---------------------------------------------------------------------------// -// Apply preconditioner -//---------------------------------------------------------------------------// -void CusolverGLU::check_status(cusolverStatus_t stat, - const std::string& msg) const -{ - cudaDeviceSynchronize(); - if (stat == CUSOLVER_STATUS_ALLOC_FAILED) - { - std::stringstream ss; - ss << msg << " failed to allocate device memory" << stat; - throw std::runtime_error(ss.str()); - } - else if (stat != CUSOLVER_STATUS_SUCCESS) - { - std::stringstream ss; - ss << msg << " failed with status " << stat; - throw std::runtime_error(ss.str()); - } -} - -//---------------------------------------------------------------------------// -// Trilinos interface functions that vary with versioning -//---------------------------------------------------------------------------// -int CusolverGLU::num_local_rows() const -{ -#if TRILINOS_MAJOR_MINOR_VERSION >= 130400 - return _A->getLocalNumRows(); -#else - return _A->getNodeNumRows(); -#endif -} - -std::size_t CusolverGLU::num_local_entries() const -{ -#if TRILINOS_MAJOR_MINOR_VERSION >= 130400 - return _A->getLocalNumEntries(); -#else - return _A->getNodeNumEntries(); -#endif -} - -std::size_t CusolverGLU::max_entries_per_row() const -{ -#if TRILINOS_MAJOR_MINOR_VERSION >= 130400 - return _A->getLocalMaxNumRowEntries(); -#else - return _A->getNodeMaxNumRowEntries(); -#endif -} - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.hpp deleted file mode 100644 index e09b547..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverGLU.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_CUSOLVERGLU_HPP -#define VERTEXCFD_LINEARSOLVERS_CUSOLVERGLU_HPP - -#include "VertexCFD_LinearSolvers_CusolverNonpublic.hpp" -#include "VertexCFD_LinearSolvers_LocalDirectSolver.hpp" - -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Local preconditioner/solver using cuSOLVER GLU for on-GPU solves. -// This class uses the nonpublic GLU solver from the NVIDIA cuSOLVER library. -// This is a refactor-based solver where an initial factorization is performed -// on the host (using a corresponding cuSOLVER routine) and subsequent -// factorizations are performed on the GPU. The initial host-side -// factorization can be reused across multiple nonlinear iterations within a -// time step as well as multiple time steps. This process assumes that -// 1) the sparsity pattern of the matrix does not change, and 2) the locations -// of any necessary pivoting that are determined by the host-side -// factorization remain valid for all subsequent GPU solves. Effectively, -// the GPU solves are utilizing static pivoting. The solver does not currently -// attempt to recover if a factorization breaks down due to pivots becoming -// stale/invalid. -//---------------------------------------------------------------------------// -class CusolverGLU : public LocalDirectSolver -{ - private: - // >>> DATA - - // Matrix - Teuchos::RCP> _A; - - // Device-side matrix data - thrust::device_vector _A_rowptr; - thrust::device_vector _A_colind; - thrust::device_vector _A_values; - - // Host-side matrix data - std::vector _A_rowptr_host; - std::vector _A_colind_host; - std::vector _A_values_host; - - // Pivoting data - double _pivot_threshold; - int _reorder; - - // Scratch space - thrust::device_vector _work; - - // Persistent Cusparse info - cusolverSpHandle_t _handle; - csrluInfoHost_t _lu_info; - csrgluInfo_t _M_info; - cusparseMatDescr_t _A_descr, _M_descr; - - // Status flags - bool _matrix_set; - bool _initialized; - bool _computed; - - public: - // Constructor - CusolverGLU(const Teuchos::ParameterList& params); - - // Update internal matrix - void setMatrix(Teuchos::RCP> A) override; - - // Inherited interface from LocalDirectSolver - void initialize() override; - void compute() override; - - // Inherited interface from LocalDirectSolver - void - solve(const Tpetra::MultiVector<>& b, Tpetra::MultiVector<>& x) override; - - private: - // Check status condition and throw exception if failed - void - check_status(cusolverStatus_t stat, const std::string& identifier) const; - - // Get number of rows in matrix - int num_local_rows() const; - std::size_t num_local_entries() const; - std::size_t max_entries_per_row() const; -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_CUSOLVERGLU_HPP diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverNonpublic.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_CusolverNonpublic.hpp deleted file mode 100644 index 4191be0..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_CusolverNonpublic.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_CUSOLVERNONPUBLIC_HPP -#define VERTEXCFD_LINEARSOLVERS_CUSOLVERNONPUBLIC_HPP - -#include -#include -#include -#include - -#include - -#if defined(__cplusplus) -extern "C" -{ -#endif /* __cplusplus */ - -/* - * Prototypes not in public header file - */ -cusolverStatus_t CUSOLVERAPI cusolverSpXcsrluConfigHost(csrluInfoHost_t info, - int reorder /* 0 or 1 - */ -); - -cusolverStatus_t CUSOLVERAPI cusolverSpDcsrlucondHost(csrluInfoHost_t info, - double* max_diag_u_host, - double* min_diag_u_host, - double* max_l_host); - -cusolverStatus_t CUSOLVERAPI cusolverSpXcsrluNnzMHost(cusolverSpHandle_t handle, - int* nnz_m_ref_host, - csrluInfoHost_t info); - -cusolverStatus_t CUSOLVERAPI -cusolverSpDcsrluExtractMHost(cusolverSpHandle_t handle, - int* P_host, - int* Q_host, - const cusparseMatDescr_t M_descr, - double* M_values_host, - int* M_rowptr_host, - int* M_colind_host, - csrluInfoHost_t info, - void* work_host); - -struct csrgluInfo; -typedef struct csrgluInfo* csrgluInfo_t; - -cusolverStatus_t CUSOLVERAPI cusolverSpCreateGluInfo(csrgluInfo_t* info); - -cusolverStatus_t CUSOLVERAPI cusolverSpDestroyGluInfo(csrgluInfo_t info); - -cusolverStatus_t CUSOLVERAPI -cusolverSpDgluSetup(cusolverSpHandle_t handle, - int m, - int nnzA, - const cusparseMatDescr_t A_descr, - const int* A_rowptr_host, - const int* A_colind_host, - const int* P_host, - const int* Q_host, - int M_nnz, - const cusparseMatDescr_t M_descr, - const int* M_rowptr_host, - const int* M_colind_host, - csrgluInfo_t info); - -cusolverStatus_t CUSOLVERAPI cusolverSpDgluBufferSize( - cusolverSpHandle_t handle, csrgluInfo_t info, size_t* p_buffer_size_host); - -cusolverStatus_t CUSOLVERAPI cusolverSpDgluAnalysis(cusolverSpHandle_t handle, - csrgluInfo_t info, - void* work); - -cusolverStatus_t CUSOLVERAPI cusolverSpDgluReset(cusolverSpHandle_t handle, - int num_rows, - int A_nnz, - const cusparseMatDescr_t A_descr, - const double* A_values, - const int* A_rowptr, - const int* A_colind, - csrgluInfo_t info); - -cusolverStatus_t CUSOLVERAPI cusolverSpDgluFactor(cusolverSpHandle_t handle, - csrgluInfo_t info, - void* work); - -cusolverStatus_t CUSOLVERAPI cusolverSpDgluSolve(cusolverSpHandle_t handle, - int num_rows, - int A_nnz, - const cusparseMatDescr_t A_descr, - const double* A_values, - const int* A_rowptr, - const int* A_colind, - const double* b, /* right hand - side */ - double* x, /* left hand side - */ - int* ite_refine_succ_host, - double* r_nrminf_ptr_host, - csrgluInfo_t info, - void* work); - -cusolverStatus_t CUSOLVERAPI cusolverSpDnrminf(cusolverSpHandle_t handle, - int n, - const double* x, - double* result_host, /* |x|_inf, - * host - */ - void* work /* at least 8192 - bytes */ -); - -#if defined(__cplusplus) -} -#endif /* __cplusplus */ - -#endif // VERTEXCFD_LINEARSOLVERS_CUSOLVERNONPUBLIC_CUH diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.cpp deleted file mode 100644 index d401c7d..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp" -#include "VertexCFD_LinearSolvers_PreconditionerFactory.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Build a linear op with solve -//---------------------------------------------------------------------------// -Teuchos::RCP> -LOWSFactoryBuilder::buildLOWS(Teuchos::RCP params) -{ - // The default Stratimikos solver builder recognizes Ifpack and ML - // preconditioners, but Ifpack2 and MueLu must be explicitly registered - Stratimikos::DefaultLinearSolverBuilder builder; - - { - using Base = Thyra::PreconditionerFactoryBase; - using Impl = Thyra::Ifpack2PreconditionerFactory< - Tpetra::CrsMatrix>; - - builder.setPreconditioningStrategyFactory( - Teuchos::abstractFactoryStd(), "Ifpack2"); - } - - { -#if TRILINOS_MAJOR_MINOR_VERSION >= 130500 - Stratimikos::enableMueLu(builder, "MueLu"); -#else - Stratimikos::enableMueLu( - builder, "MueLu"); -#endif - } - - { - // Register VertexCFD preconditioner factory with Stratimikos - using Base = Thyra::PreconditionerFactoryBase; - using Impl = VertexCFD::LinearSolvers::PreconditionerFactory; - builder.setPreconditioningStrategyFactory( - Teuchos::abstractFactoryStd(), "VertexCFD"); - } - - builder.setParameterList(params); - Teuchos::RCP> lowsFactory - = createLinearSolveStrategy(builder); - - return lowsFactory; -} - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp deleted file mode 100644 index f28637d..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_LOWSFactoryBuilder.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_LOWSFACTORYBUILDER_HPP -#define VERTEXCFD_LINEARSOLVERS_LOWSFACTORYBUILDER_HPP - -#include -#include - -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Build a "Linear Op With Solve" (i.e., linear solver) factory. -// This class uses the Stratimikos DefaultLinearSolverBuilder, but allows -// custom VertexCFD preconditioners to be used with a Trilinos linear solver. -//---------------------------------------------------------------------------// - -class LOWSFactoryBuilder -{ - public: - // Prevent construction - LOWSFactoryBuilder() = delete; - - // Build solver from solver name - static Teuchos::RCP> - buildLOWS(Teuchos::RCP params); -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -//---------------------------------------------------------------------------// -#endif // VERTEXCFD_LINEARSOLVERS_LOWSFACTORYBUILDER_HPP -//---------------------------------------------------------------------------// diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_LocalDirectSolver.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_LocalDirectSolver.hpp deleted file mode 100644 index fb90db4..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_LocalDirectSolver.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_LOCALDIRECTSOLVER_HPP -#define VERTEXCFD_LINEARSOLVERS_LOCALDIRECTSOLVER_HPP - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Base class for solvers performing local (non-MPI) sparse direct solves -//---------------------------------------------------------------------------// -class LocalDirectSolver -{ - public: - // Constructor - LocalDirectSolver() = default; - - // Set or change local matrix - virtual void setMatrix(Teuchos::RCP> A) = 0; - - // Perform one-time initialization (e.g., symbolic factorization) - virtual void initialize() = 0; - - // Compute factorization (every time matrix changes) - virtual void compute() = 0; - - // Given RHS vector b, solve for x - virtual void solve(const Tpetra::MultiVector<>& b, Tpetra::MultiVector<>& x) - = 0; -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_LOCALDIRECTSOLVER_HPP diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.cpp deleted file mode 100644 index b2926e9..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.cpp +++ /dev/null @@ -1,54 +0,0 @@ - -#include "VertexCFD_LinearSolvers_LocalSolverFactory.hpp" - -#ifdef __CUDACC__ -#include "VertexCFD_LinearSolvers_CusolverGLU.hpp" -#endif -#ifdef HAVE_SUPERLUDIST -#include "VertexCFD_LinearSolvers_SuperLU.hpp" -#endif - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Constructor -//---------------------------------------------------------------------------// -std::shared_ptr -LocalSolverFactory::buildSolver(const Teuchos::ParameterList& params) -{ - // Get solver name, default to Cusolver GLU - std::string name = "Cusolver GLU"; - if (params.isType("Local Solver")) - name = params.get("Local Solver"); - - if (name == "Cusolver GLU") - { -#ifdef __CUDACC__ - return std::make_shared(params); -#else - throw std::runtime_error( - "Solver option `Cusolver GLU` is not available because CUDA is " - "not enabled."); -#endif - } - else if (name == "SuperLU") -#ifdef HAVE_SUPERLUDIST - return std::make_shared(); -#else - throw std::runtime_error( - "Solver option `SuperLU` is not available because SUPERLUDIST is" - "not enabled."); -#endif - else - { - std::string msg = "Unrecognized local solver " + name; - throw std::runtime_error(msg); - } -} - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.hpp deleted file mode 100644 index 6fc9185..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_LocalSolverFactory.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_LOCALSOLVERFACTORY_HPP -#define VERTEXCFD_LINEARSOLVERS_LOCALSOLVERFACTORY_HPP - -#include "VertexCFD_LinearSolvers_LocalDirectSolver.hpp" - -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Class for managing construction of LocalDirectSolver subclasses -//---------------------------------------------------------------------------// -class LocalSolverFactory -{ - public: - // Prevent construction - LocalSolverFactory() = delete; - - // Build solver from solver name - static std::shared_ptr - buildSolver(const Teuchos::ParameterList& params); -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_LOCALSOLVERFACTORY_HPP diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.cpp deleted file mode 100644 index 8b4a989..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "VertexCFD_LinearSolvers_Preconditioner.hpp" -#include "VertexCFD_LinearSolvers_LocalSolverFactory.hpp" - -#include -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Constructor -//---------------------------------------------------------------------------// -Preconditioner::Preconditioner() - : _initialized(false) - , _computed(false) - , _num_initialize(0) - , _num_compute(0) - , _num_apply(0) - , _init_time(0.0) - , _compute_time(0.0) - , _apply_time(0.0) -{ -} - -//---------------------------------------------------------------------------// -// Set parameters governing solve and construct local solver -//---------------------------------------------------------------------------// -void Preconditioner::setParameters(const Teuchos::ParameterList& params) -{ - _local_solver = LocalSolverFactory::buildSolver(params); -} - -//---------------------------------------------------------------------------// -// Change matrix -//---------------------------------------------------------------------------// -void Preconditioner::setMatrix(const Teuchos::RCP>& A) -{ - auto timer = Teuchos::TimeMonitor::getNewTimer(_set_label); - Teuchos::TimeMonitor tm(*timer); - - // Depending on order of construction of AdditiveSchwarz, the matrix - // may be null on the first call to setMatrix. In this case, setMatrix - // will be called again later. - _A = A; - if (!_A) - return; - - _local_solver->setMatrix(_A); - _computed = false; -} - -//---------------------------------------------------------------------------// -// Initialize preconditioner (symbolic factorization) -//---------------------------------------------------------------------------// -void Preconditioner::initialize() -{ - auto timer = Teuchos::TimeMonitor::getNewTimer(_init_label); - Teuchos::TimeMonitor tm(*timer); - double start_time = Teuchos::Time::wallTime(); - - _local_solver->initialize(); - _initialized = true; - _num_initialize++; - _init_time += (Teuchos::Time::wallTime() - start_time); -} - -//---------------------------------------------------------------------------// -// Compute preconditioner (numeric factorization) -//---------------------------------------------------------------------------// -void Preconditioner::compute() -{ - auto timer = Teuchos::TimeMonitor::getNewTimer(_compute_label); - Teuchos::TimeMonitor tm(*timer); - double start_time = Teuchos::Time::wallTime(); - - _local_solver->compute(); - _computed = true; - _num_compute++; - _compute_time += (Teuchos::Time::wallTime() - start_time); -} - -//---------------------------------------------------------------------------// -// Apply preconditioner: y = alpha * P * x + beta * y -//---------------------------------------------------------------------------// -void Preconditioner::apply(const Tpetra::MultiVector<>& x, - Tpetra::MultiVector<>& y, - Teuchos::ETransp mode, - double alpha, - double beta) const -{ - auto timer = Teuchos::TimeMonitor::getNewTimer(_apply_label); - Teuchos::TimeMonitor tm(*timer); - double start_time = Teuchos::Time::wallTime(); - - _num_apply++; - - if (mode != Teuchos::NO_TRANS) - { - throw std::logic_error( - "VertexCFD preconditioner does not support apply with transpose"); - } - - // Use Belos class to perform common operations on multivectors - using MV_Traits = Belos::MultiVecTraits>; - - assert(MV_Traits::GetNumberVecs(x) == MV_Traits::GetNumberVecs(y)); - - // If alpha is zero, don't apply operator, just scale and return - if (alpha == Teuchos::ScalarTraits::zero()) - { - MV_Traits::MvScale(y, beta); - _apply_time += (Teuchos::Time::wallTime() - start_time); - return; - } - - // If beta is zero, do apply and scale - if (beta == Teuchos::ScalarTraits::zero()) - { - _local_solver->solve(x, y); - MV_Traits::MvScale(y, alpha); - } - else - { - // For nonzero beta, need temporary vector - Teuchos::RCP> z = MV_Traits::Clone(x, 1); - _local_solver->solve(x, *z); - - MV_Traits::MvAddMv(alpha, *z, beta, y, y); - } - _apply_time += (Teuchos::Time::wallTime() - start_time); -} - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.hpp deleted file mode 100644 index 910e554..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_Preconditioner.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_PRECONDITIONER_HPP -#define VERTEXCFD_LINEARSOLVERS_PRECONDITIONER_HPP - -#include "VertexCFD_LinearSolvers_LocalDirectSolver.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -/* - * This class is the Trilinos (Ifpack2) interface for local domain - * preconditioners as part of an additive Schwarz type scheme. It is assumed - * that the matrix provided via the setMatrix method will only contain the - * _local_ matrix elements with no off-processor components. - * It implements the Ifpack2::Preconditioner interface, which allows it to - * be used as the local solver within an Ifpack2::AdditiveSchwarz instance. - * This class is only an interface -- the local solve itself is handled by - * a subclass of LocalDirectSolver and built by the LocalSolverFactory. This - * design prevents every local solver class (e.g., CuSOLVER, SuperLU) from - * having to re-implement the full API inherited from Ifpack2::Preconditioner. - */ -//---------------------------------------------------------------------------// -class Preconditioner - : public Ifpack2::Preconditioner<>, - public Ifpack2::Details::CanChangeMatrix> -{ - private: - using MV = Tpetra::MultiVector<>; - - // Local matrix - Teuchos::RCP> _A; - - // Implementation of local solve - std::shared_ptr _local_solver; - - // Tracking of calls and time to init, compute, apply - bool _initialized; - bool _computed; - int _num_initialize; - int _num_compute; - mutable int _num_apply; // Must be updated from within const "apply" - double _init_time; - double _compute_time; - mutable double _apply_time; - - // Timer labels - const std::string _set_label = "VertexCFD::Preconditioner::setMatrix"; - const std::string _init_label = "VertexCFD::Preconditioner::initialize"; - const std::string _compute_label = "VertexCFD::Preconditioner::compute"; - const std::string _apply_label = "VertexCFD::Preconditioner::apply"; - - public: - // Constructor - Preconditioner(); - - // Inherited API from Ifpack2::CanChangeMatrix - void setMatrix(const Teuchos::RCP>& A) override; - - // Inherited API from Ifpack2::Preconditioner - Teuchos::RCP> getMatrix() const override - { - return _A; - } - bool isInitialized() const override { return _initialized; } - bool isComputed() const override { return _computed; } - int getNumInitialize() const override { return _num_initialize; } - int getNumCompute() const override { return _num_compute; } - int getNumApply() const override { return _num_apply; } - double getInitializeTime() const override { return _init_time; } - double getComputeTime() const override { return _compute_time; } - double getApplyTime() const override { return _apply_time; } - Teuchos::RCP> getDomainMap() const override - { - return _A->getDomainMap(); - } - Teuchos::RCP> getRangeMap() const override - { - return _A->getRangeMap(); - } - void setParameters(const Teuchos::ParameterList& pl) override; - void initialize() override; - void compute() override; - void apply(const Tpetra::MultiVector<>& x, - Tpetra::MultiVector<>& y, - Teuchos::ETransp mode, - double alpha, - double beta) const override; -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_PRECONDITIONER_HPP diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.cpp deleted file mode 100644 index e8dd6ad..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "VertexCFD_LinearSolvers_PreconditionerFactory.hpp" -#include "VertexCFD_LinearSolvers_Preconditioner.hpp" - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Determine if preconditioner is compatible with specified operator -//---------------------------------------------------------------------------// -bool PreconditionerFactory::isCompatible( - const Thyra::LinearOpSourceBase& fwd_op_src) const -{ - auto fwd_op = fwd_op_src.getOp(); - - auto thyra_tpetra_op = Teuchos::rcp_dynamic_cast< - const Thyra::TpetraLinearOp>(fwd_op); - if (Teuchos::is_null(thyra_tpetra_op)) - return false; - - auto tpetra_op = thyra_tpetra_op->getConstTpetraOperator(); - auto tpetra_row_mat - = Teuchos::rcp_dynamic_cast>(tpetra_op); - - return Teuchos::nonnull(tpetra_row_mat); -} - -//---------------------------------------------------------------------------// -// Construct (but do not initialize) preconditioner -//---------------------------------------------------------------------------// -Teuchos::RCP> -PreconditionerFactory::createPrec() const -{ - return Teuchos::rcp(new Thyra::DefaultPreconditioner()); -} - -//---------------------------------------------------------------------------// -// Initialize preconditioner -//---------------------------------------------------------------------------// -void PreconditionerFactory::initializePrec( - const Teuchos::RCP>& fwd_op_src, - Thyra::PreconditionerBase* prec_op, - const Thyra::ESupportSolveUse /* not used */) const - -{ - // - // Extract raw Tpetra matrix - // - - using RowMatrix = Tpetra::RowMatrix<>; - - auto fwd_op = fwd_op_src->getOp(); - auto thyra_tpetra_op = Teuchos::rcp_dynamic_cast< - const Thyra::TpetraLinearOp>(fwd_op); - auto tpetra_op = thyra_tpetra_op->getConstTpetraOperator(); - auto tpetra_row_mat = Teuchos::rcp_dynamic_cast(tpetra_op); - - // Build Additive Schwarz preconditioner - if (!_schwarz) - { - // Build "outer" preconditiner - _schwarz = Teuchos::rcp( - new Ifpack2::AdditiveSchwarz(tpetra_row_mat)); - _schwarz->setParameters(*_params); - - // Build "inner" preconditioner and compute - auto inner_prec_params = Teuchos::sublist( - _params, "schwarz: inner preconditioner parameters"); - auto inner_prec = Teuchos::rcp(new Preconditioner()); - inner_prec->setParameters(*inner_prec_params); - _schwarz->setInnerPreconditioner(inner_prec); - _schwarz->initialize(); - _schwarz->compute(); - - // Wrap additive Schwarz into a Thyra::Preconditioner - auto thyra_schwarz - = Thyra::createLinearOp(_schwarz); - - // Cast input arg to Thyra::DefaultPreconditioner and set operator - auto* default_prec - = dynamic_cast*>(prec_op); - default_prec->initializeUnspecified(thyra_schwarz); - } - else - { - _schwarz->setMatrix(tpetra_row_mat); - _schwarz->initialize(); - _schwarz->compute(); - return; - } -} - -//---------------------------------------------------------------------------// -// Uninitialize preconditioner -//---------------------------------------------------------------------------// -void PreconditionerFactory::uninitializePrec( - Thyra::PreconditionerBase*, - Teuchos::RCP>*, - Thyra::ESupportSolveUse*) const -{ -} - -//---------------------------------------------------------------------------// -// Set parameters -//---------------------------------------------------------------------------// -void PreconditionerFactory::setParameterList( - const Teuchos::RCP& params) -{ - _params = params; -} - -//---------------------------------------------------------------------------// -// Return ParameterList -//---------------------------------------------------------------------------// -Teuchos::RCP -PreconditionerFactory::getNonconstParameterList() -{ - return _params; -} - -//---------------------------------------------------------------------------// -// Clear existing parameters -//---------------------------------------------------------------------------// -Teuchos::RCP PreconditionerFactory::unsetParameterList() -{ - auto old_params = _params; - _params = Teuchos::null; - return old_params; -} - -//---------------------------------------------------------------------------// -// Get valid parameters -//---------------------------------------------------------------------------// -Teuchos::RCP -PreconditionerFactory::getValidParameters() const -{ - // Get parameters for AdditiveSchwarz first - auto params = Teuchos::rcp(new Teuchos::ParameterList()); - if (_schwarz) - { - *params = *(_schwarz->getValidParameters()); - } - else - { - Teuchos::RCP> row_mat; - auto schwarz = Teuchos::rcp( - new Ifpack2::AdditiveSchwarz>(row_mat)); - *params = *(schwarz->getValidParameters()); - } - - // Add VertexCFD preconditioner parameters for inner solver - auto inner_params - = Teuchos::sublist(params, "schwarz: inner preconditioner parameters"); - inner_params->set("Local Solver", "Cusolver GLU"); - inner_params->set("Reorder", 1); - inner_params->set("Pivot Threshold", 1.0e-2); - - return params; -} - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.hpp deleted file mode 100644 index 7c23f48..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_PreconditionerFactory.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_PRECONDITIONERFACTORY_HPP -#define VERTEXCFD_LINEARSOLVERS_PRECONDITIONERFACTORY_HPP - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Build a VertexCFD Preconditioner. -// This class allows for the construction of "custom" preconditioners that -// aren't supported through Trilinos. -//---------------------------------------------------------------------------// -class PreconditionerFactory : public Thyra::PreconditionerFactoryBase -{ - public: - // Determine if preconditioner is compatible with specified operator - bool isCompatible( - const Thyra::LinearOpSourceBase& fwdOpSrc) const override; - - // Construct (but do not initialize) preconditioner - Teuchos::RCP> createPrec() const override; - - // Initialize preconditioner - void initializePrec( - const Teuchos::RCP>& fwdOpSrc, - Thyra::PreconditionerBase* precOp, - const Thyra::ESupportSolveUse supportSolveUse - = Thyra::SUPPORT_SOLVE_UNSPECIFIED) const override; - - void uninitializePrec( - Thyra::PreconditionerBase* prec, - Teuchos::RCP>* fwdOpSrc = NULL, - Thyra::ESupportSolveUse* supportSolveUse = NULL) const override; - - // - // Teuchos::ParameterListAcceptor API - // - void setParameterList( - const Teuchos::RCP& params) override; - Teuchos::RCP getNonconstParameterList() override; - Teuchos::RCP unsetParameterList() override; - - Teuchos::RCP - getValidParameters() const override; - - private: - Teuchos::RCP _params; - - mutable Teuchos::RCP>> _schwarz; -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_PRECONDITIONERFACTORY_HPP diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.cpp b/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.cpp deleted file mode 100644 index 4026593..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "VertexCFD_LinearSolvers_SuperLU.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Constructor -//---------------------------------------------------------------------------// -SuperLUSolver::SuperLUSolver() - : _matrix_set(false) - , _initialized(false) - , _computed(false) - , _factored(false) -{ - // setup grid 1 x 1 x 1 for single task MPI solve - superlu_gridinit(MPI_COMM_SELF, 1, 1, &_grid); - - // set SuperLU solver options - set_default_options_dist(&_options); - //_options.Algo3d = YES; - // 0: turn off row permutation - _options.RowPerm = (rowperm_t)0; - // 4: Use METIS ordering on A'+A - _options.ColPerm = (colperm_t)4; - - _options.IterRefine = NOREFINE; - _options.PrintStat = NO; - - // print out SuperLU version - int comm_rank; - MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank); - if (comm_rank == 0) - { - int v_major, v_minor, v_bugfix; - superlu_dist_GetVersionNumber(&v_major, &v_minor, &v_bugfix); - std::cout << "Uses SuperLU version " << v_major << "." << v_minor - << "." << v_bugfix << std::endl; - print_options_dist(&_options); - fflush(stdout); - } -} - -SuperLUSolver::~SuperLUSolver() -{ - Destroy_CompRowLoc_Matrix_dist(&_A); - dDestroy_LU(_num_rows, &_grid, &_LUstruct); - - dScalePermstructFree(&_ScalePermstruct); - dLUstructFree(&_LUstruct); - if (_options.SolveInitialized) - { - dSolveFinalize(&_options, &_SOLVEstruct); - } - - superlu_gridexit(&_grid); -} - -//---------------------------------------------------------------------------// -// Set matrix -//---------------------------------------------------------------------------// -void SuperLUSolver::setMatrix(Teuchos::RCP> A) -{ - if (_matrix_set) - { - // WARNING: - // destroying _A also destroys the vectors _A_rowptr_host, - // _A_colind_host and _A_values_host! - Destroy_CompRowLoc_Matrix_dist(&_A); - dDestroy_LU(_num_rows, &_grid, &_LUstruct); - } - - _num_rows = A->getNodeNumRows(); - - // Host-side allocation for CRS data - // Since SuperLU will free these arrays when destroying matrix using its - // own wrappers, let's use SuperLU wrappers to allocate that memory to - // ensure consistency - int nnz_loc = A->getNodeNumEntries(); - _A_rowptr_host = (int*)SUPERLU_MALLOC((_num_rows + 1) * sizeof(int)); - _A_colind_host = (int*)SUPERLU_MALLOC(nnz_loc * sizeof(int)); - _A_values_host = (double*)SUPERLU_MALLOC(nnz_loc * sizeof(double)); - - // Extract matrix data from A - std::size_t num_entries; - Teuchos::Array row_inds; - Teuchos::Array row_vals; - _A_rowptr_host[0] = 0; - int offset = 0; - for (int row = 0; row < _num_rows; ++row) - { - num_entries = A->getNumEntriesInLocalRow(row); - row_inds.resize(num_entries); - row_vals.resize(num_entries); - A->getLocalRowCopy(row, row_inds, row_vals, num_entries); - memcpy(_A_colind_host + offset, - row_inds.data(), - num_entries * sizeof(int)); - memcpy(_A_values_host + offset, - row_vals.data(), - num_entries * sizeof(double)); - _A_rowptr_host[1 + row] = _A_rowptr_host[row] + num_entries; - offset += num_entries; - } - - // create SuperLU matrix - dCreate_CompRowLoc_Matrix_dist(&_A, - _num_rows, - _num_rows, - nnz_loc, - _num_rows, - 0, - _A_values_host, - _A_colind_host, - _A_rowptr_host, - SLU_NR_loc, - SLU_D, - SLU_GE); - - if (_factored) - _options.Fact = SamePattern; // tell SuperLU new matrix has same nnz - // pattern - else - _options.Fact = DOFACT; - - _matrix_set = true; - _factored = false; -} - -//---------------------------------------------------------------------------// -// Initialize preconditioner -//---------------------------------------------------------------------------// -void SuperLUSolver::initialize() -{ - assert(_matrix_set); - - if (_initialized) - { - return; - } - - // Initialize SuperLU structs: ScalePermstruct and LUstruct - // Needs to be done only once since saprsity pattern will not change - dScalePermstructInit(_num_rows, _num_rows, &_ScalePermstruct); - dLUstructInit(_num_rows, &_LUstruct); - - _initialized = true; -} - -//---------------------------------------------------------------------------// -// Compute preconditioner -//---------------------------------------------------------------------------// -void SuperLUSolver::compute() -{ - _computed = true; -} - -//---------------------------------------------------------------------------// -// Compute and apply preconditioner -//---------------------------------------------------------------------------// -void SuperLUSolver::solve(const Tpetra::MultiVector<>& b, - Tpetra::MultiVector<>& x) -{ - assert(_matrix_set); - assert(_initialized); - - Kokkos::fence(); - - // copy b into x - Tpetra::deep_copy(x, b); - - // Get Kokkos Views - x.clear_sync_state(); - auto b_view = b.getLocalViewHost(Tpetra::Access::ReadOnly); - - auto xvec = x.getVectorNonConst(0); - auto x_view = xvec->getLocalViewHost(Tpetra::Access::OverwriteAllStruct()); - - int info; - - // Initialize SuperLU statistics variables - SuperLUStat_t stat; - PStatInit(&stat); - - double berr; - - // factorize and solve linear system - // by using Gaussian elimination with static pivoting - // Output: - // xv is overwritten with the solution - // A is overwritten by the scaled and permuted - // matrix diag(R)*A*diag(C)*Pc^T - // Solver expects and returns data on the CPU - // Detailed info in SuperLU_dist file: SRC/pdgssvx.c - double* xv = x_view.data(); - - if (_factored) - _options.Fact = FACTORED; // factored form of A is supplied - - pdgssvx(&_options, - &_A, - &_ScalePermstruct, - xv, - _num_rows, - 1, - &_grid, - &_LUstruct, - &_SOLVEstruct, - &berr, - &stat, - &info); - - x.modify_host(); - x.sync_device(); - if (info) - { // Something is wrong - std::stringstream ss; - ss << "ERROR: INFO = " << info << " returned from pdgssvx3d()"; - throw std::runtime_error(ss.str()); - } - - // Print statistics - // PStatPrint (&_options, &stat, &_grid); - - PStatFree(&stat); - - _factored = true; -} - -} // namespace LinearSolvers -} // namespace VertexCFD diff --git a/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.hpp b/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.hpp deleted file mode 100644 index 8c02edf..0000000 --- a/src/linear_solvers/VertexCFD_LinearSolvers_SuperLU.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef VERTEXCFD_LINEARSOLVERS_SUPERLUSOLVER_HPP -#define VERTEXCFD_LINEARSOLVERS_SUPERLUSOLVER_HPP - -#include "VertexCFD_LinearSolvers_LocalDirectSolver.hpp" - -// superlu_ddefs.h includes C macro that conflicts with some -// cuda libraries include files. So let's include it after -// everything else -#define GPU_ACC -#include - -namespace VertexCFD -{ -namespace LinearSolvers -{ -//---------------------------------------------------------------------------// -// Local preconditioner/solver using SuperLU for on-GPU solves. -//---------------------------------------------------------------------------// -class SuperLUSolver : public LocalDirectSolver -{ - static_assert(std::is_same::value, - "SuperLU should be build with int_t=int"); - - private: - // >>> DATA - - // Matrix - SuperMatrix _A; - - // Host-side matrix data - int* _A_rowptr_host; - int* _A_colind_host; - double* _A_values_host; - - // Persistent SuperLU info - gridinfo_t _grid; - superlu_dist_options_t _options; - dLUstruct_t _LUstruct; - dScalePermstruct_t _ScalePermstruct; - dSOLVEstruct_t _SOLVEstruct; - - // Status flags - bool _matrix_set; - bool _initialized; - bool _computed; - bool _factored; - - int _num_rows; - - public: - // Constructor - SuperLUSolver(); - - ~SuperLUSolver(); - - // Update internal matrix - void setMatrix(Teuchos::RCP> A) override; - - // Inherited interface from LocalDirectSolver - void initialize() override; - void compute() override; - - // Inherited interface from LocalDirectSolver - void - solve(const Tpetra::MultiVector<>& b, Tpetra::MultiVector<>& x) override; -}; - -//---------------------------------------------------------------------------// - -} // namespace LinearSolvers -} // namespace VertexCFD - -#endif // VERTEXCFD_LINEARSOLVERS_SUPERLUSOLVER_HPP diff --git a/src/linear_solvers/unit_test/CMakeLists.txt b/src/linear_solvers/unit_test/CMakeLists.txt deleted file mode 100644 index 596fd62..0000000 --- a/src/linear_solvers/unit_test/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -# Components inside of linear_solvers currently only have -# CUDA implementation -if(${VERTEXCFD_KOKKOS_DEVICE_TYPE} STREQUAL "CUDA") - VertexCFD_add_tests( - LIBS VertexCFD - NAMES - CusolverGLU - LocalSolverFactory - Preconditioner - PreconditionerFactory - ) -endif() - diff --git a/src/linear_solvers/unit_test/VertexCFD_SolverTester.hpp b/src/linear_solvers/unit_test/VertexCFD_SolverTester.hpp deleted file mode 100644 index 19b5b36..0000000 --- a/src/linear_solvers/unit_test/VertexCFD_SolverTester.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#include - -#include -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -class SolverTester : public ::testing::Test -{ - protected: - void SetUp() - { - using GO = Tpetra::Details::DefaultTypes::global_ordinal_type; - using LO = Tpetra::Details::DefaultTypes::local_ordinal_type; - - auto comm = Teuchos::rcp(new Teuchos::MpiComm(MPI_COMM_WORLD)); - LO nx = 10; - LO ny = 10; - GO num_global_entries = nx * ny; - LO index_base = 0; - _map = Teuchos::rcp( - new Tpetra::Map<>(num_global_entries, index_base, comm)); - - LO entries_per_row = 5; - _matrix = Teuchos::rcp(new Tpetra::CrsMatrix<>(_map, entries_per_row)); - - // Build 2D Laplacian with Dirichlet BCs - Teuchos::ArrayRCP row_inds(entries_per_row); - Teuchos::ArrayRCP row_vals(entries_per_row); -#if TRILINOS_MAJOR_MINOR_VERSION >= 130400 - LO num_local_rows = _map->getLocalNumElements(); -#else - LO num_local_rows = _map->getNodeNumElements(); -#endif - for (LO local_row = 0; local_row < num_local_rows; ++local_row) - { - GO global_row = _map->getGlobalElement(local_row); - GO ix = global_row % nx; - GO iy = global_row / nx; - - // Diagonal entry - row_inds[0] = global_row; - row_vals[0] = 4.0; - int num_entries = 1; - - if (ix > 0) - { - row_inds[num_entries] = ix - 1 + iy * nx; - row_vals[num_entries] = -1.0; - num_entries++; - } - if (ix < (nx - 1)) - { - row_inds[num_entries] = ix + 1 + iy * nx; - row_vals[num_entries] = -1.0; - num_entries++; - } - if (iy > 0) - { - row_inds[num_entries] = ix + (iy - 1) * nx; - row_vals[num_entries] = -1.0; - num_entries++; - } - if (iy < (ny - 1)) - { - row_inds[num_entries] = ix + (iy + 1) * nx; - row_vals[num_entries] = -1.0; - num_entries++; - } - - _matrix->insertGlobalValues(global_row, - row_inds.view(0, num_entries), - row_vals.view(0, num_entries)); - } - _matrix->fillComplete(); - - // Build RHS and solution vector - _x = Teuchos::rcp(new Tpetra::MultiVector<>(_map, 1)); - _x->putScalar(1.0); - _y = Teuchos::rcp(new Tpetra::MultiVector<>(_map, 1)); - _y->putScalar(0.0); - - // Store reference solution -- this is result of solving a linear - // system with above matrix with a right hand side of all ones. - // Solution computed using numpy - _ref_soln - = {1.342423770482685, 2.18484754096537, 2.723654789583171, - 3.0479959507414374, 3.201077948227338, 3.201077948227339, - 3.047995950741439, 2.723654789583172, 2.1848475409653703, - 1.3424237704826851, 2.1848475409653694, 3.6733116037956237, - 4.6617756666258785, 5.267251065155242, 5.5552378939405775, - 5.555237893940578, 5.267251065155244, 4.661775666625878, - 3.6733116037956237, 2.1848475409653707, 2.723654789583172, - 4.661775666625879, 5.982885207969479, 6.803994749313078, - 7.19738466843915, 7.197384668439149, 6.803994749313074, - 5.982885207969476, 4.6617756666258785, 2.7236547895831706, - 3.0479959507414387, 5.267251065155243, 6.803994749313077, - 7.768458055688435, 8.232921362063793, 8.232921362063792, - 7.76845805568843, 6.803994749313073, 5.26725106515524, - 3.047995950741437, 3.2010779482273386, 5.555237893940577, - 7.19738466843915, 8.232921362063792, 8.732921362063793, - 8.732921362063792, 8.232921362063788, 7.197384668439146, - 5.555237893940573, 3.2010779482273364, 3.201077948227339, - 5.555237893940577, 7.197384668439149, 8.23292136206379, - 8.73292136206379, 8.732921362063788, 8.232921362063786, - 7.197384668439144, 5.555237893940571, 3.201077948227336, - 3.0479959507414396, 5.267251065155243, 6.803994749313074, - 7.76845805568843, 8.232921362063788, 8.232921362063786, - 7.768458055688429, 6.80399474931307, 5.267251065155238, - 3.0479959507414365, 2.723654789583173, 4.661775666625879, - 5.982885207969477, 6.803994749313072, 7.1973846684391445, - 7.197384668439141, 6.8039947493130715, 5.982885207969472, - 4.661775666625878, 2.72365478958317, 2.1848475409653707, - 3.6733116037956255, 4.661775666625879, 5.267251065155241, - 5.555237893940573, 5.555237893940573, 5.267251065155241, - 4.661775666625878, 3.673311603795623, 2.18484754096537, - 1.3424237704826854, 2.1848475409653703, 2.7236547895831715, - 3.047995950741438, 3.201077948227337, 3.2010779482273364, - 3.0479959507414374, 2.7236547895831706, 2.1848475409653694, - 1.342423770482685}; - } - - void TearDown() {} - - void solve() {} - - protected: - Teuchos::RCP> _map; - Teuchos::RCP> _matrix; - Teuchos::RCP> _x; - Teuchos::RCP> _y; - std::vector _ref_soln; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/linear_solvers/unit_test/tstCusolverGLU.cpp b/src/linear_solvers/unit_test/tstCusolverGLU.cpp deleted file mode 100644 index 7119563..0000000 --- a/src/linear_solvers/unit_test/tstCusolverGLU.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -class CusolverGLUTester : public SolverTester -{ - protected: - void solve() const - { - // Build and compute preconditioner - Teuchos::ParameterList params; - VertexCFD::LinearSolvers::CusolverGLU solver(params); - solver.setMatrix(_matrix); - solver.initialize(); - solver.compute(); - - // In operator notation, the input vector "x" becomes the RHS for a - // linear solve and the output "y" is the result - solver.solve(*_x, *_y); - } -}; - -//---------------------------------------------------------------------------// -TEST_F(CusolverGLUTester, solve_test) -{ - this->solve(); - - auto y_data = _y->getData(0); - int num_local_rows = y_data.size(); - double tol = 1e-14; - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - EXPECT_NEAR(_ref_soln[local_row], y_data[local_row], tol); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/linear_solvers/unit_test/tstLocalSolverFactory.cpp b/src/linear_solvers/unit_test/tstLocalSolverFactory.cpp deleted file mode 100644 index 9619c1c..0000000 --- a/src/linear_solvers/unit_test/tstLocalSolverFactory.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -TEST(LocalSolverFactoryTester, build_test) -{ - // Test with default parameters - Teuchos::ParameterList params; - auto solver - = VertexCFD::LinearSolvers::LocalSolverFactory::buildSolver(params); - auto cusolver - = std::dynamic_pointer_cast( - solver); - EXPECT_TRUE(cusolver != nullptr); - - // Test with solver name - params.set("Local Solver", "Cusolver GLU"); - solver = VertexCFD::LinearSolvers::LocalSolverFactory::buildSolver(params); - cusolver = std::dynamic_pointer_cast( - solver); - EXPECT_TRUE(cusolver != nullptr); - - // Test with invalid name - params.set("Local Solver", "Foo"); - EXPECT_THROW( - VertexCFD::LinearSolvers::LocalSolverFactory::buildSolver(params), - std::runtime_error); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/linear_solvers/unit_test/tstPreconditioner.cpp b/src/linear_solvers/unit_test/tstPreconditioner.cpp deleted file mode 100644 index 8f6a9fd..0000000 --- a/src/linear_solvers/unit_test/tstPreconditioner.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -class PreconditionerTester : public SolverTester -{ -}; - -//---------------------------------------------------------------------------// -TEST_F(PreconditionerTester, solve_test) -{ - // Build and compute preconditioner - Teuchos::ParameterList params; - VertexCFD::LinearSolvers::Preconditioner prec; - prec.setParameters(params); - EXPECT_FALSE(prec.isInitialized()); - EXPECT_FALSE(prec.isComputed()); - EXPECT_EQ(0, prec.getNumInitialize()); - EXPECT_EQ(0, prec.getNumCompute()); - EXPECT_EQ(0, prec.getNumApply()); - - // Set matrix - prec.setMatrix(_matrix); - EXPECT_FALSE(prec.isInitialized()); - EXPECT_FALSE(prec.isComputed()); - EXPECT_EQ(0, prec.getNumInitialize()); - EXPECT_EQ(0, prec.getNumCompute()); - EXPECT_EQ(0, prec.getNumApply()); - - // Initialize preconditioner - prec.initialize(); - EXPECT_TRUE(prec.isInitialized()); - EXPECT_FALSE(prec.isComputed()); - EXPECT_EQ(1, prec.getNumInitialize()); - EXPECT_EQ(0, prec.getNumCompute()); - EXPECT_EQ(0, prec.getNumApply()); - - // Compute preconditioner - prec.compute(); - EXPECT_TRUE(prec.isInitialized()); - EXPECT_TRUE(prec.isComputed()); - EXPECT_EQ(1, prec.getNumInitialize()); - EXPECT_EQ(1, prec.getNumCompute()); - EXPECT_EQ(0, prec.getNumApply()); - - // Apply preconditioner with beta = 0. - // Existing values in y should be ignored - _x->putScalar(1.0); - _y->putScalar(2.0); - prec.apply(*_x, *_y, Teuchos::NO_TRANS, 1.0, 0.0); - EXPECT_TRUE(prec.isInitialized()); - EXPECT_TRUE(prec.isComputed()); - EXPECT_EQ(1, prec.getNumInitialize()); - EXPECT_EQ(1, prec.getNumCompute()); - EXPECT_EQ(1, prec.getNumApply()); - - { - auto y_data = _y->getData(0); - int num_local_rows = y_data.size(); - double tol = 1e-14; - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - EXPECT_NEAR(_ref_soln[local_row], y_data[local_row], tol); - } - } - - // Apply preconditioner with alpha = 0. - // Values in x should be ignored. No solve is performed, just scaling on y. - _x->putScalar(1.0); - _y->putScalar(2.0); - prec.apply(*_x, *_y, Teuchos::NO_TRANS, 0.0, 1.5); - EXPECT_TRUE(prec.isInitialized()); - EXPECT_TRUE(prec.isComputed()); - EXPECT_EQ(1, prec.getNumInitialize()); - EXPECT_EQ(1, prec.getNumCompute()); - EXPECT_EQ(2, prec.getNumApply()); - - { - auto y_data = _y->getData(0); - int num_local_rows = y_data.size(); - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - EXPECT_DOUBLE_EQ(3.0, y_data[local_row]); - } - } - - // Apply preconditioner with nonzero alpha and beta. - // Values in both x and y are used. - _x->putScalar(1.0); - _y->putScalar(2.0); - prec.apply(*_x, *_y, Teuchos::NO_TRANS, 2.0, 3.0); - EXPECT_TRUE(prec.isInitialized()); - EXPECT_TRUE(prec.isComputed()); - EXPECT_EQ(1, prec.getNumInitialize()); - EXPECT_EQ(1, prec.getNumCompute()); - EXPECT_EQ(3, prec.getNumApply()); - - { - auto y_data = _y->getData(0); - int num_local_rows = y_data.size(); - double tol = 2e-14; - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - EXPECT_NEAR( - 2.0 * _ref_soln[local_row] + 6.0, y_data[local_row], tol); - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/linear_solvers/unit_test/tstPreconditionerFactory.cpp b/src/linear_solvers/unit_test/tstPreconditionerFactory.cpp deleted file mode 100644 index 2bb5d56..0000000 --- a/src/linear_solvers/unit_test/tstPreconditionerFactory.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -class PreconditionerFactoryTester : public SolverTester -{ -}; - -//---------------------------------------------------------------------------// -TEST_F(PreconditionerFactoryTester, build_test) -{ - using panzer::GlobalOrdinal; - using panzer::TpetraNodeType; - - // Wrap matrix into Thyra operator - auto thyra_op - = Thyra::createLinearOp( - _matrix); - auto thyra_op_src - = Teuchos::rcp(new Thyra::DefaultLinearOpSource(thyra_op)); - - // Create factory - VertexCFD::LinearSolvers::PreconditionerFactory factory; - auto params = Teuchos::rcp(new Teuchos::ParameterList()); - factory.setParameterList(params); - EXPECT_TRUE(factory.isCompatible(*thyra_op_src)); - - // Create preconditioner and initialize - auto prec = factory.createPrec(); - EXPECT_TRUE(prec != Teuchos::null); - factory.initializePrec(thyra_op_src, prec.get()); - - // Check that operator was initialized correctly within preconditioner - auto unspecified_prec_op = prec->getUnspecifiedPrecOp(); - EXPECT_TRUE(unspecified_prec_op != Teuchos::null); - - // Extract Thyra operator - auto thyra_tpetra_prec = Teuchos::rcp_dynamic_cast< - const Thyra::TpetraLinearOp>( - unspecified_prec_op); - EXPECT_TRUE(thyra_tpetra_prec != Teuchos::null); - auto tpetra_prec = thyra_tpetra_prec->getConstTpetraOperator(); - EXPECT_TRUE(tpetra_prec != Teuchos::null); - - // Check if operator is AdditiveSchwarz - auto schwarz = Teuchos::rcp_dynamic_cast< - const Ifpack2::AdditiveSchwarz>>(tpetra_prec); - EXPECT_TRUE(schwarz != Teuchos::null); - EXPECT_TRUE(schwarz->isInitialized()); - EXPECT_TRUE(schwarz->isComputed()); - - // Perform operator apply - schwarz->apply(*_x, *_y, Teuchos::NO_TRANS, 1.0, 0.0); - - // Compare against reference solution - auto y_data = _y->getData(0); - int num_local_rows = y_data.size(); - double tol = 1e-14; - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - EXPECT_NEAR(_ref_soln[local_row], y_data[local_row], tol); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/mesh/VertexCFD_Mesh_ExodusWriter.cpp b/src/mesh/VertexCFD_Mesh_ExodusWriter.cpp deleted file mode 100644 index abe92a3..0000000 --- a/src/mesh/VertexCFD_Mesh_ExodusWriter.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "VertexCFD_Mesh_ExodusWriter.hpp" - -#include "utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp" - -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -void ExodusWriter::add_mesh_outputs(const Teuchos::ParameterList& params, - const OutputType output_type, - const OutputLocation output_location) -{ - for (const auto& param : params) - { - const auto& block_id = param.first; - const auto tokens = VectorizeOutputFieldNames::tokenizeParameter(param); - for (const auto& field : tokens) - { - if (output_location == OutputLocation::Node) - { - // nodal scalar - _mesh->addSolutionField(field, block_id); - } - else if (output_type == OutputType::Scalar) - { - // cell scalar - _mesh->addCellField(field, block_id); - } - else - { - // cell vector - constexpr char dim_name[3] = {'X', 'Y', 'Z'}; - for (std::size_t dim = 0; dim < _mesh->getDimension(); ++dim) - { - _mesh->addCellField(field + dim_name[dim], block_id); - } - } - } - } -} - -//---------------------------------------------------------------------------// -ExodusWriter::ExodusWriter( - const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& response_library, - const Teuchos::ParameterList& output_params) - : _mesh(mesh) - , _dof_manager(dof_manager) - , _lof(lof) - , _response_library(response_library) -{ - add_mesh_outputs(output_params.sublist("Cell Average Quantities"), - OutputType::Scalar, - OutputLocation::Cell); - add_mesh_outputs(output_params.sublist("Cell Average Vectors"), - OutputType::Vector, - OutputLocation::Cell); - add_mesh_outputs(output_params.sublist("Cell Quantities"), - OutputType::Scalar, - OutputLocation::Cell); - add_mesh_outputs(output_params.sublist("Nodal Quantities"), - OutputType::Scalar, - OutputLocation::Node); - - _mesh->setupExodusFile( - output_params.template get("Exodus Output File")); - - std::vector element_blocks; - _mesh->getElementBlockNames(element_blocks); - panzer_stk::RespFactorySolnWriter_Builder response_builder; - response_builder.mesh = mesh; - - _response_library->addResponse( - "Main Field Output", element_blocks, response_builder); -} - -//---------------------------------------------------------------------------// -void ExodusWriter::writeSolution( - const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot, - const double time, - const double time_step) -{ - panzer::AssemblyEngineInArgs in_args; - in_args.container_ = _lof->buildLinearObjContainer(); - in_args.ghostedContainer_ = _lof->buildGhostedLinearObjContainer(); - in_args.alpha = 0.0; - in_args.beta = 1.0; - in_args.time = time; - in_args.step_size = time_step; - in_args.evaluate_transient_terms = true; - - _lof->initializeGhostedContainer( - panzer::LinearObjContainer::X | panzer::LinearObjContainer::DxDt, - *(in_args.ghostedContainer_)); - - auto thyra_container - = Teuchos::rcp_dynamic_cast>( - in_args.container_, true); - thyra_container->set_x_th( - Teuchos::rcp_const_cast>(x)); - thyra_container->set_dxdt_th( - Teuchos::rcp_const_cast>(x_dot)); - - _response_library->addResponsesToInArgs(in_args); - _response_library->evaluate(in_args); - - _mesh->writeToExodus(time); -} - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD diff --git a/src/mesh/VertexCFD_Mesh_ExodusWriter.hpp b/src/mesh/VertexCFD_Mesh_ExodusWriter.hpp deleted file mode 100644 index a9fb942..0000000 --- a/src/mesh/VertexCFD_Mesh_ExodusWriter.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef VERTEXCFD_MESH_EXODUSWRITER_HPP -#define VERTEXCFD_MESH_EXODUSWRITER_HPP - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -class ExodusWriter -{ - public: - ExodusWriter( - const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& - response_library, - const Teuchos::ParameterList& output_params); - - void - writeSolution(const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot, - const double time = 0.0, - const double time_step = 0.0); - - private: - enum class OutputType - { - Scalar, - Vector - }; - - enum class OutputLocation - { - Node, - Cell - }; - - void add_mesh_outputs(const Teuchos::ParameterList& params, - const OutputType output_type, - const OutputLocation output_location); - - Teuchos::RCP _mesh; - Teuchos::RCP _dof_manager; - Teuchos::RCP> _lof; - Teuchos::RCP> _response_library; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD - -#endif // end VERTEXCFD_MESH_EXODUSWRITER_HPP diff --git a/src/mesh/VertexCFD_Mesh_GeometryData.hpp b/src/mesh/VertexCFD_Mesh_GeometryData.hpp deleted file mode 100644 index bb9b603..0000000 --- a/src/mesh/VertexCFD_Mesh_GeometryData.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef VERTEXCFD_MESH_GEOMETRYDATA_HPP -#define VERTEXCFD_MESH_GEOMETRYDATA_HPP - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace Mesh -{ -namespace Topology -{ - -class SidesetGeometry -{ - public: - // Constructor - SidesetGeometry(const Teuchos::RCP& mesh, - std::vector walls) - { - // Get names of sidesets. Return on empty input. - std::vector sideset_block_names; - mesh->getSidesetNames(sideset_block_names); - if (sideset_block_names.size() == 0) - return; - - std::vector e_block_names; - mesh->getElementBlockNames(e_block_names); - - // Get topology information and ensure that the topology is Tet4's - for (long unsigned int block = 0; block < e_block_names.size(); ++block) - { - _topology = mesh->getCellTopology(e_block_names[block]); - if (_topology->getKey() != shards::Tetrahedron<4>::key - && _topology->getKey() != shards::Hexahedron<8>::key - && _topology->getKey() != shards::Triangle<3>::key - && _topology->getKey() != shards::Quadrilateral<4>::key) - { - const std::string msg = "Block topology is not Tet4 or Tri3"; - throw std::runtime_error(msg); - } - } - - // Get the local set of sides declared as walls from all block/sideset - // combinations. - std::vector local_side_entities; - for (const auto& sb : sideset_block_names) - { - for (long unsigned int i = 0; i < walls.size(); ++i) - { - if (walls[i] == sb) - { - std::vector sideset_sides; - mesh->getMySides(sb, sideset_sides); - local_side_entities.insert(std::end(local_side_entities), - std::begin(sideset_sides), - std::end(sideset_sides)); - break; - } - } - } - - Kokkos::DynRankView local_sides; - mesh->getElementVertices(local_side_entities, local_sides); - - // Extract the mpi communicator. - auto comm = mesh->getComm(); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*comm); - - // Get extents. - int num_space_dim = _topology->getDimension(); - int nodes_per_side = _topology->getVertexCount(num_space_dim - 1, 0); - int side_data_count = nodes_per_side * num_space_dim; - - // Compose gather communication pattern. - std::vector global_counts(comm->getSize(), 0); - int receive_counts; - receive_counts = local_sides.extent(0); - MPI_Allgather(&receive_counts, - 1, - MPI_INT, - global_counts.data(), - 1, - MPI_INT, - mpi_comm); - std::vector receive_displacements(comm->getSize(), 0); - int global_num_side = 0; - for (int r = 0; r < comm->getSize(); ++r) - { - receive_displacements[r] = global_num_side * side_data_count; - global_num_side += global_counts[r]; - } - - for (int rank = 0; rank < comm->getSize(); ++rank) - { - global_counts[rank] *= side_data_count; - } - - // Combine all sides into a replicated global set of sides on each - // rank so we can build a replicated, non-distributed tree on each - // rank. - _global_sides = Kokkos::View( - Kokkos::ViewAllocateWithoutInitializing("Global side Nodes"), - global_num_side, - nodes_per_side, - num_space_dim); - MPI_Allgatherv(local_sides.data(), - local_sides.size(), - MPI_DOUBLE, - _global_sides.data(), - global_counts.data(), - receive_displacements.data(), - MPI_DOUBLE, - mpi_comm); - } - - // Get the side topology. - Teuchos::RCP topology() const - { - return _topology; - } - - // Get the sides. - Kokkos::View sides() const - { - return _global_sides; - } - - private: - // Side topology. - Teuchos::RCP _topology; - - // Sides given as coordinates (side,node,dim) - Kokkos::View _global_sides; -}; - -} // end namespace Topology -} // end namespace Mesh -} // end namespace VertexCFD - -#endif // end VERTEXCFD_MESH_GEOMETRYDATA diff --git a/src/mesh/VertexCFD_Mesh_GeometryPrimitives.hpp b/src/mesh/VertexCFD_Mesh_GeometryPrimitives.hpp deleted file mode 100644 index 0f1ba22..0000000 --- a/src/mesh/VertexCFD_Mesh_GeometryPrimitives.hpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - Function for calculating the wall distance from an arbitrary point - to an arbitrary (triangular) surface and/or (linear) edge -*/ -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace GeometryPrimitives -{ - -/* - Calculates the cross product of two 3x1 vectors -*/ -KOKKOS_INLINE_FUNCTION void -crossProduct(double arr1[3], double arr2[3], double arr_out[3]) -{ - arr_out[0] = arr1[1] * arr2[2] - arr1[2] * arr2[1]; - arr_out[1] = arr1[2] * arr2[0] - arr1[0] * arr2[2]; - arr_out[2] = arr1[0] * arr2[1] - arr1[1] * arr2[0]; -} - -/* - Checks if the intersection of a point, p, with a plane, defined ba * ca, lies - within the triangle abc, and if it is outside the triangle what edge it lies - closest to -*/ -template -KOKKOS_INLINE_FUNCTION int tetCheck(SideViewType& sides_view, - NormalViewType& normals_view, - int side, - PointViewType& ip_view, - int cell, - int point, - int index[3]) -{ - // calculate the intersection location of the point and plane along the - // normal vector - double t1 = normals_view(side, 0) * sides_view(side, index[0], 0) - + normals_view(side, 1) * sides_view(side, index[0], 1) - + normals_view(side, 2) * sides_view(side, index[0], 2); - double t2 = normals_view(side, 0) * ip_view(cell, point, 0) - + normals_view(side, 1) * ip_view(cell, point, 1) - + normals_view(side, 2) * ip_view(cell, point, 2); - double param = t1 - t2; - - double intersection_point[3]; - double line_a_int[3]; - double line_b_int[3]; - double line_c_int[3]; - double line_ba[3]; - double line_cb[3]; - double line_ac[3]; - - for (int dim = 0; dim < 3; ++dim) - { - // Intersection point with the plane the side lies in - intersection_point[dim] = ip_view(cell, point, dim) - + param * normals_view(side, dim); - // Line segment from intersection point to node A - line_a_int[dim] = intersection_point[dim] - - sides_view(side, index[0], dim); - // Line segment from node B to node A - line_ba[dim] = sides_view(side, index[1], dim) - - sides_view(side, index[0], dim); - // Line segment from intersection point to node B - line_b_int[dim] = intersection_point[dim] - - sides_view(side, index[1], dim); - // Line segment from node C to node B - line_cb[dim] = sides_view(side, index[2], dim) - - sides_view(side, index[1], dim); - // Line segment from intersection point to node C - line_c_int[dim] = intersection_point[dim] - - sides_view(side, index[2], dim); - // Line segment from node A to node C - line_ac[dim] = sides_view(side, index[0], dim) - - sides_view(side, index[2], dim); - } - - // calculate unit normal vector of triangle abp - double norm_abp[3]; - crossProduct(line_ba, line_b_int, norm_abp); - double n_abp_mag = sqrt(norm_abp[0] * norm_abp[0] - + norm_abp[1] * norm_abp[1] - + norm_abp[2] * norm_abp[2]); - if (n_abp_mag != 0) - { - for (int dim = 0; dim < 3; ++dim) - { - norm_abp[dim] /= n_abp_mag; - } - } - - // calculate unit normal vector of triangle bcp - double norm_bcp[3]; - crossProduct(line_cb, line_c_int, norm_bcp); - double n_bcp_mag = sqrt(norm_bcp[0] * norm_bcp[0] - + norm_bcp[1] * norm_bcp[1] - + norm_bcp[2] * norm_bcp[2]); - if (n_bcp_mag != 0) - { - for (int dim = 0; dim < 3; ++dim) - { - norm_bcp[dim] /= n_bcp_mag; - } - } - - // calculate unit normal vector of triangle cap - double norm_cap[3]; - crossProduct(line_ac, line_a_int, norm_cap); - double n_cap_mag = sqrt(norm_cap[0] * norm_cap[0] - + norm_cap[1] * norm_cap[1] - + norm_cap[2] * norm_cap[2]); - if (n_cap_mag != 0) - { - for (int dim = 0; dim < 3; ++dim) - { - norm_cap[dim] /= n_cap_mag; - } - } - - int tet_flag = -1; - - double abp = norm_abp[0] * normals_view(side, 0) - + norm_abp[1] * normals_view(side, 1) - + norm_abp[2] * normals_view(side, 2); - double bcp = norm_bcp[0] * normals_view(side, 0) - + norm_bcp[1] * normals_view(side, 1) - + norm_bcp[2] * normals_view(side, 2); - double cap = norm_cap[0] * normals_view(side, 0) - + norm_cap[1] * normals_view(side, 1) - + norm_cap[2] * normals_view(side, 2); - - if (abp > 0.01 && bcp > 0.01 && cap > 0.01) - { - tet_flag = 0; - } - else if (abp <= 0.01 && cap > 0.01 && bcp > 0.01) - { - tet_flag = 1; - } - else if (bcp <= 0.01 && cap > 0.01 && abp > 0.01) - { - tet_flag = 2; - } - else if (cap <= 0.01 && abp > 0.01 && bcp > 0.01) - { - tet_flag = 3; - } - else if (abp <= 0.01 && bcp <= 0.01) - { - tet_flag = 4; - } - else if (bcp <= 0.01 && cap <= 0.01) - { - tet_flag = 5; - } - else if (cap <= 0.01 && abp <= 0.01) - { - tet_flag = 6; - } - - return tet_flag; -} - -/* - Determines the distance between a point, p, and a plane defined by a point a - and a normal n -*/ -template -KOKKOS_INLINE_FUNCTION double planeIntersect(SideViewType& sides_view, - NormalViewType& normals_view, - int side, - PointViewType& ip_view, - int cell, - int point) -{ - // calculate n_hat * a - n_hat * p, which defines the distance to the - // interaction point - double t1 = normals_view(side, 0) * sides_view(side, 0, 0) - + normals_view(side, 1) * sides_view(side, 0, 1) - + normals_view(side, 2) * sides_view(side, 0, 2); - double t2 = normals_view(side, 0) * ip_view(cell, point, 0) - + normals_view(side, 1) * ip_view(cell, point, 1) - + normals_view(side, 2) * ip_view(cell, point, 2); - double param = t1 - t2; - double distance = std::abs(param); - - return distance; -} - -/* - Function that returns the distance to a specified edge from a specified point - p is the test point, p0 is one end of the linear edge given, and p1 is the - other end of the linear edge given -*/ -template -KOKKOS_INLINE_FUNCTION double distanceToLinearEdge(SideViewType& sides_view, - int side, - PointViewType& ip_view, - int cell, - int point, - int num_space_dim, - int index[2]) -{ - double line_ap[3]; - double line_ab[3]; - double num = 0.0; - double denom = 0.0; - - for (int dim = 0; dim < num_space_dim; ++dim) - { - // Line between point 0 and the test point - line_ap[dim] = ip_view(cell, point, dim) - - sides_view(side, index[0], dim); - // Line between point 0 and point 1 - line_ab[dim] = sides_view(side, index[1], dim) - - sides_view(side, index[0], dim); - num += line_ap[dim] * line_ab[dim]; - denom += line_ab[dim] * line_ab[dim]; - } - - double param = -1; - // don't test case where the line has 0 length - if (denom != 0) - param = num / denom; - - double intersection_point[3]; - - if (param < 0) - { - // the point is closest to end p0 - for (int dim = 0; dim < num_space_dim; ++dim) - { - intersection_point[dim] = sides_view(side, index[0], dim); - } - } - else if (param > 1) - { - // the point is closest to end p1 - for (int dim = 0; dim < num_space_dim; ++dim) - { - intersection_point[dim] = sides_view(side, index[1], dim); - } - } - else - { - // the point "p" is closest to a point "param" along line p0-p1 - for (int dim = 0; dim < num_space_dim; ++dim) - { - intersection_point[dim] = sides_view(side, index[0], dim) - + param * line_ab[dim]; - } - } - - double distance = 0.0; - for (int dim = 0; dim < num_space_dim; ++dim) - { - distance += pow(ip_view(cell, point, dim) - intersection_point[dim], 2); - } - return sqrt(distance); -} - -/* - Function that returns the nearest distance between a triangle and a point -*/ -template -KOKKOS_INLINE_FUNCTION double -distanceToTriangleFace(SideViewType& sides_view, - NormalViewType& normals_view, - int side, - PointViewType& ip_view, - int cell, - int point, - int index[3]) -{ - // Define vectors that store the line between the first point on the - // triangle and the other two - double line_ba[3]; - double line_ca[3]; - for (int dim = 0; dim < 3; ++dim) - { - line_ba[dim] = sides_view(side, index[1], dim) - - sides_view(side, index[0], dim); - line_ca[dim] = sides_view(side, index[2], dim) - - sides_view(side, index[0], dim); - } - - // Calculate the triangle unit normal vector - double normal_abc[3]; - crossProduct(line_ba, line_ca, normal_abc); - normals_view(side, 0) = normal_abc[0]; - normals_view(side, 1) = normal_abc[1]; - normals_view(side, 2) = normal_abc[2]; - double normal_mag - = std::sqrt(normals_view(side, 0) * normals_view(side, 0) - + normals_view(side, 1) * normals_view(side, 1) - + normals_view(side, 2) * normals_view(side, 2)); - normals_view(side, 0) /= normal_mag; - normals_view(side, 1) /= normal_mag; - normals_view(side, 2) /= normal_mag; - - // calculate if "p" is closest to the triangle face, or one of the sides - auto int_flag = tetCheck( - sides_view, normals_view, side, ip_view, cell, point, index); - - double distance = 1e8; - double d_ab, d_bc, d_ca; - int node[2]; - switch (int_flag) - { - case 0: - // point "p" is closest to a point on the triangle interior - distance = planeIntersect( - sides_view, normals_view, side, ip_view, cell, point); - break; - case 1: - // point "p" is closest to a point on the edge ab - node[0] = index[0]; - node[1] = index[1]; - distance = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - break; - case 2: - // point "p" is closest to a point on the edge bc - node[0] = index[1]; - node[1] = index[2]; - distance = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - break; - case 3: - // point "p" is closest to a point on the edge ca - node[0] = index[2]; - node[1] = index[0]; - distance = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - break; - case 4: - // point "p" is closest to a point on the edge ab or bc - node[0] = index[0]; - node[1] = index[1]; - d_ab = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - node[0] = index[1]; - node[1] = index[2]; - d_bc = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - distance = std::fmin(d_ab, d_bc); - break; - case 5: - // point "p" is closest to a point on the edge bc or ca - node[0] = index[1]; - node[1] = index[2]; - d_bc = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - node[0] = index[2]; - node[1] = index[0]; - d_ca = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - distance = std::fmin(d_ca, d_bc); - break; - case 6: - // point "p" is closest to a point on the edge ab or ca - node[0] = index[0]; - node[1] = index[1]; - d_ab = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - node[0] = index[2]; - node[1] = index[0]; - d_ca = distanceToLinearEdge( - sides_view, side, ip_view, cell, point, 3, node); - distance = std::fmin(d_ab, d_ca); - break; - } - - return distance; -} - -} // end namespace GeometryPrimitives -} // end namespace VertexCFD diff --git a/src/mesh/VertexCFD_Mesh_Restart.cpp b/src/mesh/VertexCFD_Mesh_Restart.cpp deleted file mode 100644 index 8897c1a..0000000 --- a/src/mesh/VertexCFD_Mesh_Restart.cpp +++ /dev/null @@ -1,717 +0,0 @@ -#include "VertexCFD_Mesh_Restart.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -// Base -//---------------------------------------------------------------------------// -MPI_Datatype Restart::setupDofMapData( - const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - int& dofmap_offset, - int& local_num_own_elem, - std::vector& owned_element_lids) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - dof_manager->getComm()); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*comm); - const int comm_rank = comm->getRank(); - const int comm_size = comm->getSize(); - - // Compute the maximum number of dofs per element. - std::vector block_ids; - dof_manager->getElementBlockIds(block_ids); - int max_num_dof = 0; - for (auto& block : block_ids) - { - max_num_dof = std::max(max_num_dof, - dof_manager->getElementBlockGIDCount(block)); - } - dofmap_offset = max_num_dof + 1; - - // Create an ordered list of the elements that we own. - std::vector owned_elements; - mesh->getMyElements(owned_elements); - local_num_own_elem = owned_elements.size(); - std::vector owned_element_gids(local_num_own_elem); - for (int i = 0; i < local_num_own_elem; ++i) - { - owned_element_gids[i] = mesh->elementGlobalId(owned_elements[i]); - } - std::sort(owned_element_gids.begin(), owned_element_gids.end()); - - // On rank zero determine how many elements each rank owns. - std::vector elems_per_rank; - if (0 == comm_rank) - { - elems_per_rank.resize(comm_size); - } - MPI_Gather(&local_num_own_elem, - 1, - MPI_INT, - elems_per_rank.data(), - 1, - MPI_INT, - 0, - mpi_comm); - - // Gather the global ids to rank zero. - const uint64_t global_num_elem - = mesh->getEntityCounts(stk::topology::ELEM_RANK); - std::vector all_element_ids; - std::vector rank_displacements; - if (0 == comm_rank) - { - all_element_ids.resize(global_num_elem); - rank_displacements.resize(comm_size); - rank_displacements[0] = 0; - std::partial_sum(elems_per_rank.begin(), - elems_per_rank.end() - 1, - rank_displacements.begin() + 1); - } - MPI_Gatherv(owned_element_gids.data(), - owned_element_gids.size(), - MPI_UINT64_T, - all_element_ids.data(), - elems_per_rank.data(), - rank_displacements.data(), - MPI_UINT64_T, - 0, - mpi_comm); - - // Sort the list of global ids on rank zero and determine the sorted - // offset of each element into the global dof map. - if (0 == comm_rank) - { - std::vector sorted_index(global_num_elem); - std::iota(sorted_index.begin(), sorted_index.end(), 0); - std::sort(sorted_index.begin(), - sorted_index.end(), - [&](uint64_t i, uint64_t j) { - return all_element_ids[i] < all_element_ids[j]; - }); - for (uint64_t i = 0; i < global_num_elem; ++i) - { - all_element_ids[sorted_index[i]] = i; - } - } - - // Scatter the sorted gids back to the owners so they know how to write to - // their part of the dofmap. - std::vector sorted_elem_positions(local_num_own_elem); - MPI_Scatterv(all_element_ids.data(), - elems_per_rank.data(), - rank_displacements.data(), - MPI_UINT64_T, - sorted_elem_positions.data(), - sorted_elem_positions.size(), - MPI_UINT64_T, - 0, - mpi_comm); - - // Create the local element ids in the sorted order and the displacments. - owned_element_lids.resize(local_num_own_elem); - std::vector displacements(local_num_own_elem); - for (int i = 0; i < local_num_own_elem; ++i) - { - owned_element_lids[i] = mesh->elementLocalId(owned_element_gids[i]); - displacements[i] = dofmap_offset * sorted_elem_positions[i]; - } - - // Create the MPI datatype for the dof map. - // Make indexed data types into which we will write the local data. - MPI_Datatype indexed; - MPI_Type_create_indexed_block(displacements.size(), - dofmap_offset, - displacements.data(), - MPI_UINT64_T, - &indexed); - - // Update the extent of the datatype. This new data type will need to be - // committed. - MPI_Aint extent = global_num_elem * dofmap_offset * sizeof(uint64_t); - MPI_Datatype dofmap_type; - MPI_Type_create_resized(indexed, 0, extent, &dofmap_type); - MPI_Type_free(&indexed); - return dofmap_type; -} - -//---------------------------------------------------------------------------// -// Writer -// NOTE: allow_dofmap_overwrite is false by default and should be provided only -// in unit tests. -//---------------------------------------------------------------------------// -RestartWriter::RestartWriter( - const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::ParameterList& output_params, - const bool allow_dofmap_overwrite) - : _dof_manager(dof_manager) - , _file_prefix(output_params.get("Restart File Prefix")) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - dof_manager->getComm()); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*comm); - const int comm_rank = comm->getRank(); - - // Setup dof map data. - int dofmap_offset; - int local_num_own_elem; - std::vector owned_element_lids; - MPI_Datatype dofmap_type = this->setupDofMapData(mesh, - dof_manager, - dofmap_offset, - local_num_own_elem, - owned_element_lids); - MPI_Type_commit(&dofmap_type); - - // Map global element id to global dof ids. - std::vector element_dofs; - std::vector dofmap(local_num_own_elem * dofmap_offset); - int elem_local_offset; - int elem_num_dof; - for (int i = 0; i < local_num_own_elem; ++i) - { - // Get the element dofs. - _dof_manager->getElementGIDs(owned_element_lids[i], element_dofs); - elem_num_dof = element_dofs.size(); - - // Map DOF ids to the STK mesh element id. - elem_local_offset = dofmap_offset * i; - dofmap[elem_local_offset] = elem_num_dof; - for (int d = 0; d < elem_num_dof; ++d) - { - dofmap[elem_local_offset + d + 1] = element_dofs[d]; - } - - // Fill the unused dof map spots with a known quantity. - for (int d = elem_num_dof; d < dofmap_offset - 1; ++d) - { - dofmap[elem_local_offset + d + 1] - = std::numeric_limits::max(); - } - } - - // Open a binary data file for the dof map. - const std::string dofmap_file_name = _file_prefix + ".restart.dofmap"; - MPI_File dofmap_file; - { - int file_mode = MPI_MODE_WRONLY | MPI_MODE_CREATE; - if (!allow_dofmap_overwrite) - file_mode |= MPI_MODE_EXCL; - - const int error_code = MPI_File_open(mpi_comm, - dofmap_file_name.c_str(), - file_mode, - MPI_INFO_NULL, - &dofmap_file); - if (MPI_SUCCESS != error_code) - { - char error_string[MPI_MAX_ERROR_STRING + 1]{}; - int error_string_len = 0; - MPI_Error_string(error_code, error_string, &error_string_len); - error_string[error_string_len] = 0; - - std::string msg{"Error creating DOF Map file `" + dofmap_file_name - + "' : "}; - msg += error_string; - - throw std::runtime_error(msg); - } - } - - // Write the dof map offset into the header on rank 0. - MPI_File_set_view( - dofmap_file, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); - MPI_Offset header_size; - if (0 == comm_rank) - { - MPI_File_write( - dofmap_file, &dofmap_offset, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_get_position(dofmap_file, &header_size); - MPI_File_get_byte_offset(dofmap_file, header_size, &header_size); - } - - // Broadcast the header size. - MPI_Bcast(&header_size, 1, MPI_OFFSET, 0, mpi_comm); - - // Write dof map to file. - MPI_File_set_view(dofmap_file, - header_size, - MPI_UINT64_T, - dofmap_type, - "native", - MPI_INFO_NULL); - MPI_File_write_all(dofmap_file, - dofmap.data(), - dofmap.size(), - MPI_UINT64_T, - MPI_STATUS_IGNORE); - - // Cleanup. - MPI_File_close(&dofmap_file); - MPI_Type_free(&dofmap_type); - - // Make indexed data types into which we will write the local data. - std::vector gids; - _dof_manager->getOwnedIndices(gids); - _displacements.resize(gids.size()); - std::copy(gids.begin(), gids.end(), _displacements.begin()); - - // Construct map from owned global ids back to local ordering - for (int i = 0; i < _dof_manager->getNumOwned(); ++i) - _global_to_local.insert({gids[i], i}); - - // Sort displacements - std::sort(_displacements.begin(), _displacements.end()); - MPI_Datatype indexed; - MPI_Type_create_indexed_block( - _displacements.size(), 1, _displacements.data(), MPI_DOUBLE, &indexed); - - // Update the extent of the datatype. - uint64_t global_size = gids.size(); - MPI_Allreduce( - MPI_IN_PLACE, &global_size, 1, MPI_UINT64_T, MPI_SUM, mpi_comm); - MPI_Aint extent = global_size * sizeof(double); - MPI_Type_create_resized(indexed, 0, extent, &_dof_type); - MPI_Type_free(&indexed); - MPI_Type_commit(&_dof_type); -} - -//---------------------------------------------------------------------------// -RestartWriter::~RestartWriter() -{ - MPI_Type_free(&_dof_type); -} - -//---------------------------------------------------------------------------// -void RestartWriter::writeSolution( - const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot, - const int index, - const double time) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - _dof_manager->getComm()); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*comm); - const int comm_rank = comm->getRank(); - - // SpmdVectorBase is a common base class of either Epetra or Tpetra - // implementations that has a concept of local vs. global portions - // of a vector. - auto x_spmd - = Teuchos::rcp_dynamic_cast>(x); - auto x_dot_spmd - = Teuchos::rcp_dynamic_cast>(x_dot); - auto spmd_space = x_spmd->spmdSpace(); - const int local_size = spmd_space->localSubDim(); - const int global_size = spmd_space->dim(); - - // Check parallel data. - if (local_size != _dof_manager->getNumOwned()) - { - throw std::logic_error( - "Thyra::VectorBase and panzer::GlobalIndexer local sizes do not " - "match"); - } - - // Open a binary data file. - std::stringstream file_name; - file_name << _file_prefix << "_" << index; - std::string restart_file_name = file_name.str() + ".restart.data"; - MPI_File restart_file; - MPI_File_open(mpi_comm, - restart_file_name.c_str(), - MPI_MODE_WRONLY | MPI_MODE_CREATE, - MPI_INFO_NULL, - &restart_file); - - // Write the time into the header on rank 0. - const int num_fields = _dof_manager->getNumFields(); - MPI_File_set_view( - restart_file, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); - MPI_Offset header_size; - if (0 == comm_rank) - { - MPI_File_write(restart_file, &time, 1, MPI_DOUBLE, MPI_STATUS_IGNORE); - MPI_File_write( - restart_file, &num_fields, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_write( - restart_file, &global_size, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_get_position(restart_file, &header_size); - MPI_File_get_byte_offset(restart_file, header_size, &header_size); - } - - // Broadcast the header size. - MPI_Bcast(&header_size, 1, MPI_OFFSET, 0, mpi_comm); - - // Reorder the state vector to correspond to the increasing order - // displacements. - auto x_view = x_spmd->getLocalSubVector(); - std::vector x_copy(local_size); - for (int i = 0; i < local_size; ++i) - { - const int local_index = _global_to_local[_displacements[i]]; - x_copy[i] = x_view(local_index); - } - - // Write state vector. - MPI_File_set_view(restart_file, - header_size, - MPI_DOUBLE, - _dof_type, - "native", - MPI_INFO_NULL); - MPI_File_write_all(restart_file, - x_copy.data(), - x_copy.size(), - MPI_DOUBLE, - MPI_STATUS_IGNORE); - - // Reorder the state vector time derivative to correspond to the - // increasing order displacements. - auto x_dot_view = x_dot_spmd->getLocalSubVector(); - std::vector x_dot_copy(local_size); - for (int i = 0; i < local_size; ++i) - { - const int local_index = _global_to_local[_displacements[i]]; - x_dot_copy[i] = x_dot_view(local_index); - } - - // Write state vector time derivative. - MPI_File_write_all(restart_file, - x_dot_copy.data(), - x_dot_copy.size(), - MPI_DOUBLE, - MPI_STATUS_IGNORE); - - // Cleanup. - MPI_File_close(&restart_file); -} - -//---------------------------------------------------------------------------// -// Reader -//---------------------------------------------------------------------------// -RestartReader::RestartReader(const Teuchos::RCP>& comm, - const Teuchos::ParameterList& input_params) - : _restart_file_name(input_params.get("Restart Data File " - "Name")) - , _dofmap_file_name(input_params.get("Restart DOF Map File " - "Name")) -{ - // Get the MPI communicator. - auto mcomm = Teuchos::rcp_dynamic_cast>(comm); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*mcomm); - - // Open the restart file. - MPI_File restart_file; - const int error_code = MPI_File_open(mpi_comm, - _restart_file_name.c_str(), - MPI_MODE_RDONLY, - MPI_INFO_NULL, - &restart_file); - - if (MPI_SUCCESS != error_code) - { - std::string msg = "\n\nThe restart file " + _restart_file_name - + "\ncould not be found in the working directory.\n"; - throw std::logic_error(msg); - } - - // Get the initial state time. - MPI_File_set_view( - restart_file, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); - MPI_File_read_all(restart_file, &_t_init, 1, MPI_DOUBLE, MPI_STATUS_IGNORE); - - // Cleanup. - MPI_File_close(&restart_file); -} - -//---------------------------------------------------------------------------// -void RestartReader::readSolution( - const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - dof_manager->getComm()); - MPI_Comm mpi_comm = Teuchos::getRawMpiComm(*comm); - - // SpmdVectorBase is a common base class of either Epetra or Tpetra - // implementations that has a concept of local vs. global portions - // of a vector. - auto x_spmd = Teuchos::rcp_dynamic_cast>(x); - auto x_dot_spmd - = Teuchos::rcp_dynamic_cast>(x_dot); - auto spmd_space = x_spmd->spmdSpace(); - const int local_size = spmd_space->localSubDim(); - const int global_size = spmd_space->dim(); - - // Check parallel data. - if (local_size != dof_manager->getNumOwned()) - { - throw std::logic_error( - "Thyra::VectorBase and panzer::GlobalIndexer local sizes do not " - "match"); - } - - // Setup dof map data. - int dofmap_offset; - int local_num_own_elem; - std::vector owned_element_lids; - MPI_Datatype dofmap_type = this->setupDofMapData(mesh, - dof_manager, - dofmap_offset, - local_num_own_elem, - owned_element_lids); - MPI_Type_commit(&dofmap_type); - - // Open the dof map file. - MPI_File dofmap_file; - const int error_code = MPI_File_open(mpi_comm, - _dofmap_file_name.c_str(), - MPI_MODE_RDONLY, - MPI_INFO_NULL, - &dofmap_file); - - if (MPI_SUCCESS != error_code) - { - std::string msg = "\n\nThe DOFMAP file " + _dofmap_file_name - + "\ncould not be found in the working directory.\n"; - throw std::logic_error(msg); - } - - // Read the dof map offset from the header on rank 0. - int file_dofmap_offset; - MPI_Offset dofmap_header_size; - MPI_File_set_view( - dofmap_file, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); - MPI_File_read_all( - dofmap_file, &file_dofmap_offset, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_get_position(dofmap_file, &dofmap_header_size); - MPI_File_get_byte_offset( - dofmap_file, dofmap_header_size, &dofmap_header_size); - if (file_dofmap_offset != dofmap_offset) - { - throw std::logic_error("DOF map offsets do not match"); - } - - // Read the local dof map. - std::vector dofmap(dofmap_offset * local_num_own_elem); - MPI_File_set_view(dofmap_file, - dofmap_header_size, - MPI_UINT64_T, - dofmap_type, - "native", - MPI_INFO_NULL); - MPI_File_read_all(dofmap_file, - dofmap.data(), - dofmap.size(), - MPI_UINT64_T, - MPI_STATUS_IGNORE); - - // Cleanup dof map file. - MPI_File_close(&dofmap_file); - MPI_Type_free(&dofmap_type); - - // Map the local dof map to the new dof manager. - std::unordered_map mapped_gids; - std::unordered_map reverse_mapped_gids; - std::vector element_dofs; - for (int i = 0; i < local_num_own_elem; ++i) - { - // Get the element dofs. - dof_manager->getElementGIDs(owned_element_lids[i], element_dofs); - const int elem_num_dof = dofmap[dofmap_offset * i]; - if (static_cast(elem_num_dof) != element_dofs.size()) - { - throw std::logic_error( - "DOF map and DOF manager element sizes do not match"); - } - - // Map to the dofs in the restart file. - for (int d = 0; d < elem_num_dof; ++d) - { - mapped_gids.emplace(element_dofs[d], - dofmap[dofmap_offset * i + d + 1]); - reverse_mapped_gids.emplace(dofmap[dofmap_offset * i + d + 1], - element_dofs[d]); - } - } - - // Open the restart file. - MPI_File restart_file; - MPI_File_open(mpi_comm, - _restart_file_name.c_str(), - MPI_MODE_RDONLY, - MPI_INFO_NULL, - &restart_file); - - // Get the header data. - MPI_Offset restart_header_size; - double time; - int num_fields; - int global_num_dof; - MPI_File_set_view( - restart_file, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); - MPI_File_read_all(restart_file, &time, 1, MPI_DOUBLE, MPI_STATUS_IGNORE); - MPI_File_read_all(restart_file, &num_fields, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_read_all( - restart_file, &global_num_dof, 1, MPI_INT, MPI_STATUS_IGNORE); - MPI_File_get_position(restart_file, &restart_header_size); - MPI_File_get_byte_offset( - restart_file, restart_header_size, &restart_header_size); - if (time != _t_init) - { - throw std::logic_error("Restart initialization times do not match"); - } - if (num_fields != dof_manager->getNumFields()) - { - throw std::logic_error("Restart number of fields do not match"); - } - if (global_num_dof != global_size) - { - throw std::logic_error("Restart global number of DOFs do not match"); - } - - // Make indexed data types from which we will read the local data. - std::vector owned_gids; - dof_manager->getOwnedIndices(owned_gids); - std::vector restart_displacements(local_size); - for (int i = 0; i < local_size; ++i) - { - restart_displacements[i] = mapped_gids.find(owned_gids[i])->second; - } - - // Create map from owned global ids back to local ids - std::unordered_map global_to_local; - for (int i = 0; i < local_size; ++i) - global_to_local.insert({owned_gids[i], i}); - - std::sort(restart_displacements.begin(), restart_displacements.end()); - MPI_Datatype restart_indexed; - MPI_Type_create_indexed_block(local_size, - 1, - restart_displacements.data(), - MPI_DOUBLE, - &restart_indexed); - - // Update the extent of the dof datatype. - MPI_Aint extent = global_size * sizeof(double); - MPI_Datatype dof_type; - MPI_Type_create_resized(restart_indexed, 0, extent, &dof_type); - MPI_Type_free(&restart_indexed); - MPI_Type_commit(&dof_type); - - // Read state vector. - std::vector x_copy(local_size); - MPI_File_set_view(restart_file, - restart_header_size, - MPI_DOUBLE, - dof_type, - "native", - MPI_INFO_NULL); - MPI_File_read_all(restart_file, - x_copy.data(), - x_copy.size(), - MPI_DOUBLE, - MPI_STATUS_IGNORE); - - // Reorder the state vector to correspond to the increasing order - // displacements. - panzer::GlobalOrdinal new_gid; - int new_lid; - auto x_view = x_spmd->getNonconstLocalSubVector(); - std::vector reordered_data(local_size); - for (int i = 0; i < local_size; ++i) - { - new_gid = reverse_mapped_gids.find(restart_displacements[i])->second; - new_lid = global_to_local[new_gid]; - reordered_data[new_lid] = x_copy[i]; - } - this->update_vector(x, reordered_data); - - // Read state vector time derivative. - std::vector x_dot_copy(local_size); - MPI_File_read_all(restart_file, - x_dot_copy.data(), - x_dot_copy.size(), - MPI_DOUBLE, - MPI_STATUS_IGNORE); - - // Reorder the state vector time derivative to correspond to the - // increasing order displacements. - for (int i = 0; i < local_size; ++i) - { - new_gid = reverse_mapped_gids.find(restart_displacements[i])->second; - new_lid = global_to_local[new_gid]; - reordered_data[new_lid] = x_dot_copy[i]; - } - this->update_vector(x_dot, reordered_data); - - // Cleanup - MPI_File_close(&restart_file); - MPI_Type_free(&dof_type); -} - -//---------------------------------------------------------------------------// -void RestartReader::update_vector( - const Teuchos::RCP>& vec, - const std::vector& values) const -{ - // Vector should be either a DefaultSpmdVector (Epetra) or a TpetraVector - auto spmd_vec - = Teuchos::rcp_dynamic_cast>(vec); - auto thyratpetra_vec = Teuchos::rcp_dynamic_cast< - Thyra::TpetraVector>( - vec); - - if (spmd_vec == Teuchos::null && thyratpetra_vec == Teuchos::null) - throw std::runtime_error("Unrecognized Thyra vector type"); - - if (spmd_vec != Teuchos::null) - { - auto space = vec->space(); - auto epetra_map = Thyra::get_Epetra_Map(space); - auto epetra_vec = Thyra::get_Epetra_Vector(vec, epetra_map); - std::copy(values.begin(), values.end(), epetra_vec->Values()); - } - else - { - auto tpetra_vec = thyratpetra_vec->getTpetraVector(); - auto data_view = tpetra_vec->getLocalViewHost( - Tpetra::Access::OverwriteAllStruct()); - std::copy(values.begin(), values.end(), data_view.data()); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD diff --git a/src/mesh/VertexCFD_Mesh_Restart.hpp b/src/mesh/VertexCFD_Mesh_Restart.hpp deleted file mode 100644 index 6c8670a..0000000 --- a/src/mesh/VertexCFD_Mesh_Restart.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef VERTEXCFD_MESH_RESTART_HPP -#define VERTEXCFD_MESH_RESTART_HPP - -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -class Restart -{ - public: - ~Restart() = default; - - MPI_Datatype - setupDofMapData(const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - int& dofmap_offset, - int& local_num_own_elem, - std::vector& owned_element_lids); -}; - -//---------------------------------------------------------------------------// -class RestartWriter : public Restart -{ - public: - // NOTE: allow_dofmap_overwrite is false by default and should be provided - // only in unit tests. - RestartWriter(const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::ParameterList& output_params, - const bool allow_dofmap_overwrite = false); - - ~RestartWriter(); - - void - writeSolution(const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot, - const int index, - const double time = 0.0); - - private: - Teuchos::RCP _dof_manager; - std::string _file_prefix; - std::vector _displacements; - std::unordered_map _global_to_local; - MPI_Datatype _dof_type; -}; - -//---------------------------------------------------------------------------// -class RestartReader : public Restart -{ - public: - RestartReader(const Teuchos::RCP>& comm, - const Teuchos::ParameterList& input_params); - - void - readSolution(const Teuchos::RCP& mesh, - const Teuchos::RCP& dof_manager, - const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot); - - double initialStateTime() const { return _t_init; } - - private: - std::string _restart_file_name; - std::string _dofmap_file_name; - double _t_init; - - void update_vector(const Teuchos::RCP>& vec, - const std::vector& data) const; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD - -#endif // end VERTEXCFD_MESH_RESTART_HPP diff --git a/src/mesh/VertexCFD_Mesh_StkReaderFactory.cpp b/src/mesh/VertexCFD_Mesh_StkReaderFactory.cpp deleted file mode 100644 index 1df3f55..0000000 --- a/src/mesh/VertexCFD_Mesh_StkReaderFactory.cpp +++ /dev/null @@ -1,505 +0,0 @@ -#include "VertexCFD_Mesh_StkReaderFactory.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -int getMeshDimension(const std::string& mesh_str, - stk::ParallelMachine parallel_mach, - const bool is_exodus) -{ - stk::io::StkMeshIoBroker mesh_data(parallel_mach); - mesh_data.property_add(Ioss::Property("LOWER_CASE_VARIABLE_NAMES", false)); - if (is_exodus) - mesh_data.add_mesh_database(mesh_str, "exodusII", stk::io::READ_MESH); - else - mesh_data.add_mesh_database(mesh_str, "cgns", stk::io::READ_MESH); - mesh_data.create_input_mesh(); - return Teuchos::as(mesh_data.meta_data().spatial_dimension()); -} - -//---------------------------------------------------------------------------// -StkReaderFactory::StkReaderFactory() - : file_name_("") - , decomp_method_("RIB") - , restart_index_(0) - , is_exodus_(true) - , user_mesh_scaling_(false) - , mesh_scale_factor_(0.0) - , levels_of_refinement_(0) -{ -} - -//---------------------------------------------------------------------------// -StkReaderFactory::StkReaderFactory(const std::string& file_name, - const int restart_index, - const bool is_exodus) - : file_name_(file_name) - , decomp_method_("RIB") - , restart_index_(restart_index) - , is_exodus_(is_exodus) - , user_mesh_scaling_(false) - , mesh_scale_factor_(0.0) - , levels_of_refinement_(0) -{ -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -StkReaderFactory::buildMesh(stk::ParallelMachine parallel_mach) const -{ - auto mesh = buildUncommitedMesh(parallel_mach); - - const bool buildRefinementSupport = levels_of_refinement_ > 0 ? true - : false; - mesh->initialize(parallel_mach, false, buildRefinementSupport); - - completeMeshConstruction(*mesh, parallel_mach); - - return mesh; -} - -//---------------------------------------------------------------------------// -/** This builds all the meta data of the mesh. Does not call metaData->commit. - * Allows user to add solution fields and other pieces. The mesh can be - * "completed" by calling completeMeshConstruction. - */ -Teuchos::RCP -StkReaderFactory::buildUncommitedMesh(stk::ParallelMachine parallel_mach) const -{ - // read in meta data - stk::io::StkMeshIoBroker* mesh_data - = new stk::io::StkMeshIoBroker(parallel_mach); - -#if TRILINOS_MAJOR_MINOR_VERSION >= 140000 - mesh_data->use_simple_fields(); -#endif - - // Tell IOSS to keep variable names as is; do not convert to lower case. - mesh_data->property_add(Ioss::Property("LOWER_CASE_VARIABLE_NAMES", false)); - - // Tell IOSS not to combine scalar fields into vector or tensor fields - // based on suffix recognition. We need scalar fields in order to use them - // as initial conditions. - mesh_data->property_add(Ioss::Property("ENABLE_FIELD_RECOGNITION", false)); - - mesh_data->property_add( - Ioss::Property("DECOMPOSITION_METHOD", decomp_method_)); - - // add in "FAMILY_TREE" entity for doing refinement - std::vector entity_rank_names = stk::mesh::entity_rank_names(); - entity_rank_names.push_back("FAMILY_TREE"); - mesh_data->set_rank_name_vector(entity_rank_names); - - if (is_exodus_) - mesh_data->add_mesh_database( - file_name_, "exodusII", stk::io::READ_MESH); - else - mesh_data->add_mesh_database(file_name_, "cgns", stk::io::READ_MESH); - - mesh_data->create_input_mesh(); -#if TRILINOS_MAJOR_MINOR_VERSION >= 130500 - auto metaData = Teuchos::rcp(mesh_data->meta_data_ptr()); -#else - auto metaData = mesh_data->meta_data_rcp(); -#endif - - auto mesh = Teuchos::rcp(new panzer_stk::STK_Interface(metaData)); - mesh->initializeFromMetaData(); - mesh->instantiateBulkData(parallel_mach); -#if TRILINOS_MAJOR_MINOR_VERSION >= 130500 - mesh_data->set_bulk_data(Teuchos::get_shared_ptr(mesh->getBulkData())); -#else - mesh_data->set_bulk_data(mesh->getBulkData()); -#endif - - // read in other transient fields, these will be useful later when - // trying to read other fields for use in solve - mesh_data->add_all_mesh_fields_as_input_fields(); - - // store mesh data pointer for later use in initializing - // bulk data - mesh->getMetaData()->declare_attribute_with_delete(mesh_data); - - // build element blocks - registerElementBlocks(*mesh, *mesh_data); - registerSidesets(*mesh); - registerNodesets(*mesh); - - mesh->addPeriodicBCs(this->periodicBCVec_); - - return mesh; -} - -//---------------------------------------------------------------------------// -void StkReaderFactory::completeMeshConstruction( - panzer_stk::STK_Interface& mesh, stk::ParallelMachine parallel_mach) const -{ - if (not mesh.isInitialized()) - { - const bool buildRefinementSupport = levels_of_refinement_ > 0 ? true - : false; - mesh.initialize(parallel_mach, true, buildRefinementSupport); - } - - // grab mesh data pointer to build the bulk data - stk::mesh::MetaData& metaData = *mesh.getMetaData(); - stk::mesh::BulkData& bulkData = *mesh.getBulkData(); - stk::io::StkMeshIoBroker* mesh_data = const_cast( - metaData.get_attribute()); - - // remove the MeshData attribute - TEUCHOS_ASSERT(metaData.remove_attribute(mesh_data)); - - // build mesh bulk data - mesh_data->populate_bulk_data(); - - // Refine - const bool delete_parent_elements = true; - if (levels_of_refinement_ > 0) - mesh.refineMesh(levels_of_refinement_, delete_parent_elements); - - // The following section of code is applicable if mesh scaling is - // turned on from the input file. - if (user_mesh_scaling_) - { -#if TRILINOS_MAJOR_MINOR_VERSION >= 140000 - stk::mesh::Field* coord_field = metaData.get_field( - stk::topology::NODE_RANK, "coordinates"); -#else - stk::mesh::Field* coord_field - = metaData.get_field>( - stk::topology::NODE_RANK, "coordinates"); -#endif - - stk::mesh::Selector select_all_local - = metaData.locally_owned_part() | metaData.globally_shared_part(); - stk::mesh::BucketVector const& my_node_buckets - = bulkData.get_buckets(stk::topology::NODE_RANK, select_all_local); - - int mesh_dim = mesh.getDimension(); - - // Scale the mesh - const double inv_msf = 1.0 / mesh_scale_factor_; - for (size_t i = 0; i < my_node_buckets.size(); ++i) - { - stk::mesh::Bucket& b = *(my_node_buckets[i]); - double* coordinate_data = field_data(*coord_field, b); - - for (size_t j = 0; j < b.size(); ++j) - { - for (int k = 0; k < mesh_dim; ++k) - { - coordinate_data[mesh_dim * j + k] *= inv_msf; - } - } - } - } - - // put in a negative index and (like python) the restart will be from the - // back (-1 is the last time step) - int restart_index = restart_index_; - if (restart_index < 0) - { -#if TRILINOS_MAJOR_MINOR_VERSION >= 140500 - std::pair lastTimeStep - = mesh_data->get_input_ioss_region()->get_max_time(); -#else - std::pair lastTimeStep - = mesh_data->get_input_io_region()->get_max_time(); -#endif - restart_index = 1 + restart_index + lastTimeStep.first; - } - - // populate mesh fields with specific index - mesh_data->read_defined_input_fields(restart_index); - - mesh.buildSubcells(); - mesh.buildLocalElementIDs(); - - if (user_mesh_scaling_) - { -#if TRILINOS_MAJOR_MINOR_VERSION >= 140000 - stk::mesh::Field* coord_field = metaData.get_field( - stk::topology::NODE_RANK, "coordinates"); -#else - stk::mesh::Field* coord_field - = metaData.get_field>( - stk::topology::NODE_RANK, "coordinates"); -#endif - std::vector fields; - fields.push_back(coord_field); - - stk::mesh::communicate_field_data(bulkData, fields); - } - - // process_input_request is a no-op if restart_index <= 0 ... thus there - // would be no inital time - if (restart_index > 0) - { -#if TRILINOS_MAJOR_MINOR_VERSION >= 140500 - mesh.setInitialStateTime( - mesh_data->get_input_ioss_region()->get_state_time(restart_index)); -#else - mesh.setInitialStateTime( - mesh_data->get_input_io_region()->get_state_time(restart_index)); -#endif - } - else - { - mesh.setInitialStateTime(0.0); - } - - // clean up mesh data object - delete mesh_data; - - // calls Stk_MeshFactory::rebalance - this->rebalance(mesh); -} - -//---------------------------------------------------------------------------// -//! From ParameterListAcceptor -void StkReaderFactory::setParameterList( - const Teuchos::RCP& param_list) -{ - TEUCHOS_TEST_FOR_EXCEPTION_PURE_MSG( - !param_list->isParameter("File Name"), - Teuchos::Exceptions::InvalidParameterName, - "Error, the parameter {name=\"File Name\"," - "type=\"string\"" - "\nis required in parameter (sub)list \"" - << param_list->name() - << "\"." - "\n\nThe parsed parameter parameter list is: \n" - << param_list->currentParametersString()); - - // Set default values here. Not all the params should be set so this - // has to be done manually as opposed to using - // validateParametersAndSetDefaults(). - if (!param_list->isParameter("Decomp Method")) - param_list->set("Decomp Method", "RIB"); - - if (!param_list->isParameter("Restart Index")) - param_list->set("Restart Index", -1); - - if (!param_list->isParameter("File Type")) - param_list->set("File Type", "Exodus"); - - if (!param_list->isSublist("Periodic BCs")) - param_list->sublist("Periodic BCs"); - - Teuchos::ParameterList& p_bcs = param_list->sublist("Periodic BCs"); - if (!p_bcs.isParameter("Count")) - p_bcs.set("Count", 0); - - if (!param_list->isParameter("Levels of Uniform Refinement")) - param_list->set("Levels of Uniform Refinement", 0); - - param_list->validateParameters(*getValidParameters(), 0); - - setMyParamList(param_list); - - file_name_ = param_list->get("File Name"); - - decomp_method_ = param_list->get("Decomp Method"); - - restart_index_ = param_list->get("Restart Index"); - - const auto file_type = param_list->get("File Type"); - is_exodus_ = (file_type == "Exodus"); - - // get any mesh scale factor - if (param_list->isParameter("Scale Factor")) - { - mesh_scale_factor_ = param_list->get("Scale Factor"); - user_mesh_scaling_ = true; - } - - // read in periodic boundary conditions -#if TRILINOS_MAJOR_MINOR_VERSION >= 130500 - parsePeriodicBCList(Teuchos::rcpFromRef(param_list->sublist("Periodic " - "BCs")), - this->periodicBCVec_, - this->useBBoxSearch_); -#else - parsePeriodicBCList(Teuchos::rcpFromRef(param_list->sublist("Periodic " - "BCs")), - this->periodicBCVec_); -#endif - - levels_of_refinement_ - = param_list->get("Levels of Uniform Refinement"); -} - -//---------------------------------------------------------------------------// -//! From ParameterListAcceptor -Teuchos::RCP -StkReaderFactory::getValidParameters() const -{ - static Teuchos::RCP validParams; - - if (validParams == Teuchos::null) - { - validParams = Teuchos::rcp(new Teuchos::ParameterList); - validParams->set( - "File Name", - "", - "Name of mesh file to be read", - Teuchos::rcp(new Teuchos::FileNameValidator)); - - validParams->set("Decomp Method", - "RIB", - "Parallel mesh decomposition method", - Teuchos::rcp(new Teuchos::StringValidator( - Ioss::valid_decomp_methods()))); - - validParams->set( - "Restart Index", - -1, - "Index of solution to read in", - Teuchos::rcp(new Teuchos::AnyNumberParameterEntryValidator( - Teuchos::AnyNumberParameterEntryValidator::PREFER_INT, - Teuchos::AnyNumberParameterEntryValidator::AcceptedTypes( - true)))); - - Teuchos::setStringToIntegralParameter( - "File Type", - "Exodus", - "Choose input file type - either \"Exodus\" or \"CGNS\"", - Teuchos::tuple("Exodus", "CGNS"), - validParams.get()); - - validParams->set( - "Scale Factor", - 1.0, - "Scale factor to apply to mesh after read", - Teuchos::rcp(new Teuchos::AnyNumberParameterEntryValidator( - Teuchos::AnyNumberParameterEntryValidator::PREFER_DOUBLE, - Teuchos::AnyNumberParameterEntryValidator::AcceptedTypes( - true)))); - - Teuchos::ParameterList& bcs = validParams->sublist("Periodic BCs"); - bcs.set("Count", 0); // no default periodic boundary conditions - - validParams->set("Levels of Uniform Refinement", - 0, - "Number of levels of inline uniform mesh refinement"); - } - - return validParams.getConst(); -} - -//---------------------------------------------------------------------------// -void StkReaderFactory::registerElementBlocks( - panzer_stk::STK_Interface& mesh, stk::io::StkMeshIoBroker& mesh_data) const -{ - auto femMetaData = mesh.getMetaData(); - - // here we use the Ioss interface because they don't add - // "bonus" element blocks and its easier to determine - // "real" element blocks versus STK-only blocks -#if TRILINOS_MAJOR_MINOR_VERSION >= 140500 - const Ioss::ElementBlockContainer& elem_blocks - = mesh_data.get_input_ioss_region()->get_element_blocks(); -#else - const Ioss::ElementBlockContainer& elem_blocks - = mesh_data.get_input_io_region()->get_element_blocks(); -#endif - for (const auto& entity : elem_blocks) - { - // Ioss::GroupingEntity* entity = *itr; - const std::string& name = entity->name(); - - const stk::mesh::Part* part = femMetaData->get_part(name); - shards::CellTopology cellTopo - = stk::mesh::get_cell_topology(femMetaData->get_topology(*part)); - const CellTopologyData* ct = cellTopo.getCellTopologyData(); - - TEUCHOS_ASSERT(ct != 0); - mesh.addElementBlock(part->name(), ct); - } -} - -//---------------------------------------------------------------------------// -void StkReaderFactory::registerSidesets(panzer_stk::STK_Interface& mesh) const -{ - Teuchos::RCP metaData = mesh.getMetaData(); - const stk::mesh::PartVector& parts = metaData->get_parts(); - - for (const auto& part : parts) - { - const stk::mesh::PartVector& subsets = part->subsets(); - shards::CellTopology cellTopo - = stk::mesh::get_cell_topology(metaData->get_topology(*part)); - const CellTopologyData* ct = cellTopo.getCellTopologyData(); - - // if a side part ==> this is a sideset: now storage is recursive - // on part contains all sub parts with consistent topology - if (part->primary_entity_rank() == mesh.getSideRank() && ct == 0 - && subsets.size() > 0) - { - TEUCHOS_TEST_FOR_EXCEPTION( - subsets.size() != 1, - std::runtime_error, - "StkReaderFactory::registerSidesets error - part \"" - << part->name() << "\" has more than one subset"); - - // grab cell topology and name of subset part - const stk::mesh::Part* ss_part = subsets[0]; - shards::CellTopology ss_cellTopo = stk::mesh::get_cell_topology( - metaData->get_topology(*ss_part)); - const CellTopologyData* ss_ct = ss_cellTopo.getCellTopologyData(); - - // only add subset parts that have no topology - if (ss_ct != 0) - mesh.addSideset(part->name(), ss_ct); - } - } -} - -//---------------------------------------------------------------------------// -void StkReaderFactory::registerNodesets(panzer_stk::STK_Interface& mesh) const -{ - Teuchos::RCP metaData = mesh.getMetaData(); - const stk::mesh::PartVector& parts = metaData->get_parts(); - - stk::mesh::PartVector::const_iterator partItr; - for (partItr = parts.begin(); partItr != parts.end(); ++partItr) - { - const stk::mesh::Part* part = *partItr; - shards::CellTopology cellTopo - = stk::mesh::get_cell_topology(metaData->get_topology(*part)); - const CellTopologyData* ct = cellTopo.getCellTopologyData(); - - // if a side part ==> this is a sideset: now storage is recursive - // on part contains all sub parts with consistent topology - if (part->primary_entity_rank() == mesh.getNodeRank() && ct == 0) - { - // only add subset parts that have no topology - if (part->name() != panzer_stk::STK_Interface::nodesString) - mesh.addNodeset(part->name()); - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD diff --git a/src/mesh/VertexCFD_Mesh_StkReaderFactory.hpp b/src/mesh/VertexCFD_Mesh_StkReaderFactory.hpp deleted file mode 100644 index f3fd522..0000000 --- a/src/mesh/VertexCFD_Mesh_StkReaderFactory.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef VERTEXCFD_MESH_STKREADERFACTORY_HPP -#define VERTEXCFD_MESH_STKREADERFACTORY_HPP - -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace Mesh -{ -//---------------------------------------------------------------------------// -/** External function to return the dimension of an Exodus or CGNS - * mesh. This uses a quick temporary read of the meta data from input - * file/mesh and is intended if you need the mesh dimension before the - * MeshFactory is used to build the uncommitted mesh. Once - * buildUncommittedMesh() is called, you can query the dimension from - * the MetaData in the STK_Interface object (will be faster than - * creating mesh metadata done in this function). - * - * \param[in] mesh_str Filename containing the mesh string, or the mesh string - * itself. - * \param[in] parallel_mach Descriptor for machine to build this mesh on. - * \param[in] is_exodus Set to true for Exodus mesh, set to false for CGNS - * mesh. - * - * \returns Integer indicating the spatial dimension of the mesh. - */ -int getMeshDimension(const std::string& mesh_str, - stk::ParallelMachine parallel_mach, - const bool is_exodus = true); - -//---------------------------------------------------------------------------// -/** Concrete mesh factory instantiation. This reads - * a mesh from an exodus file and builds a STK_Interface object. - * - * Also, if a nonzero restart index (the Exodus indices are 1 based) is - * specified then this will set the initial state time in the STK_Interface. - * However, as prescribed by that interface the currentStateTime is only - * set by the writeToExodus call, thus the currentStateTime of the created - * STK_Interface object will be zero. It is up to the user to rectify this - * when calling writeToExodus. - */ -class StkReaderFactory : public panzer_stk::STK_MeshFactory -{ - public: - StkReaderFactory(); - - /** \brief Ctor - * - * \param[in] file_name Name of the input file. - * \param[in] restart_index Index used for restarts. - * \param[in] is_exodus If true, the input file is in exodus format. If - * false, it assumes CGNS format. - */ - StkReaderFactory(const std::string& file_name, - const int restart_index = 0, - const bool is_exodus = true); - - /** Construct a STK_Inteface object described by this factory. - * - * \param[in] parallel_mach Descriptor for machine to build this mesh on. - * - * \returns Pointer to STK_Interface object with - * isModifiable()==false. - */ - virtual Teuchos::RCP - buildMesh(stk::ParallelMachine parallel_mach) const override; - - /** This builds all the meta data of the mesh. Does not call - * metaData->commit. Allows user to add solution fields and other pieces. - * The mesh can be "completed" by calling - * completeMeshConstruction. - */ - virtual Teuchos::RCP - buildUncommitedMesh(stk::ParallelMachine parallel_mach) const override; - - /** Finishes building a mesh object started by - * buildUncommitedMesh. - */ - virtual void - completeMeshConstruction(panzer_stk::STK_Interface& mesh, - stk::ParallelMachine parallel_mach) const override; - - //! From ParameterListAcceptor. Must be called if the empty ctor is used to - //! construct this object. - void setParameterList( - const Teuchos::RCP& param_list) override; - - //! From ParameterListAcceptor - Teuchos::RCP - getValidParameters() const override; - - //! Get file name mean is read from. - const std::string& getFileName() const { return file_name_; } - - protected: - void registerElementBlocks(panzer_stk::STK_Interface& mesh, - stk::io::StkMeshIoBroker& mesh_data) const; - void registerSidesets(panzer_stk::STK_Interface& mesh) const; - void registerNodesets(panzer_stk::STK_Interface& mesh) const; - - std::string file_name_; - std::string decomp_method_; - int restart_index_; - bool is_exodus_; - - private: - //! Did the user request mesh scaling - bool user_mesh_scaling_; - - //! If requested, scale the input mesh by this factor - double mesh_scale_factor_; - - //! Number of levels of inline uniform mesh refinement to be applied to - //! exodus mesh - int levels_of_refinement_; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Mesh -} // end namespace VertexCFD - -#endif // VERTEXCFD_STKREADERFACTORY_HPP diff --git a/src/mesh/unit_test/CMakeLists.txt b/src/mesh/unit_test/CMakeLists.txt deleted file mode 100644 index 7fe1991..0000000 --- a/src/mesh/unit_test/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/test_data/read_only_test.restart.data - ${CMAKE_CURRENT_BINARY_DIR}/read_only_test.restart.data - COPYONLY) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/test_data/read_only_test.restart.dofmap - ${CMAKE_CURRENT_BINARY_DIR}/read_only_test.restart.dofmap - COPYONLY) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/test_data/read_only_test_periodic.restart.data - ${CMAKE_CURRENT_BINARY_DIR}/read_only_test_periodic.restart.data - COPYONLY) - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/test_data/read_only_test_periodic.restart.dofmap - ${CMAKE_CURRENT_BINARY_DIR}/read_only_test_periodic.restart.dofmap - COPYONLY) - -VertexCFD_add_tests( - MPI - LIBS VertexCFD - NAMES Restart GeometryPrimitives - ) diff --git a/src/mesh/unit_test/test_data/.gitattributes b/src/mesh/unit_test/test_data/.gitattributes deleted file mode 100644 index 3107f4c..0000000 --- a/src/mesh/unit_test/test_data/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* !filter !diff !merge !text diff --git a/src/mesh/unit_test/test_data/read_only_test.restart.data b/src/mesh/unit_test/test_data/read_only_test.restart.data deleted file mode 100644 index c97c67faf796958ce54a94619ff294fcf06fcce2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10832 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 AOaK4? diff --git a/src/mesh/unit_test/test_data/read_only_test_periodic.restart.data b/src/mesh/unit_test/test_data/read_only_test_periodic.restart.data deleted file mode 100644 index d5eabf148d563b4c100820ab108162580032c481..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10016 zcmeHNJx>Bb5M3>)t*xQH+DvJUwLbz{tf{TFh1!aO%0g*uNJvO7B!+MigDBDwTiKhB z#6Mv2lDEyvgWchlo89>=2c3&X`6DMOrS54;H)ju*Rc|PYBN?CfzuMzR;}f~R%j3m9 zpUSSCXUy#TxirdJpv_xI>-zTT(0sM|OBv>$8?0cayV`tNH;x zOx{+iUVgw&le?2{7T5VU`FlBTqaNHxevf<~st437J;yvBysxq*@ZMFsMEgN|p8Xs8 zC8`JXuhdS`-=*K4;~2&tjH4Jw6$RsS<{6mx`A!{14a_=WVN40;O=!-z*-p_%KYQMM9mx9W@p9USho{Ns2 PXGBN!r}oj8h01>cnr%Fg diff --git a/src/mesh/unit_test/test_data/read_only_test_periodic.restart.dofmap b/src/mesh/unit_test/test_data/read_only_test_periodic.restart.dofmap deleted file mode 100644 index c9b5a464554d1e57abbe1e05d1f75472a39f73ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25004 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 YIiAz)b=Dlu>GnEXj^}hc9-rg?3nK+^ZU6uP diff --git a/src/mesh/unit_test/tstGeometryPrimitives.cpp b/src/mesh/unit_test/tstGeometryPrimitives.cpp deleted file mode 100644 index 2b9aea1..0000000 --- a/src/mesh/unit_test/tstGeometryPrimitives.cpp +++ /dev/null @@ -1,316 +0,0 @@ -#include - -#include - -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION void faceDistanceFunc(SideViewType sides, - PointViewType point, - ResultViewType result, - NormalViewType normal, - int index) -{ - int orient[3] = {0, 1, 2}; - result(index, 0) = VertexCFD::GeometryPrimitives::tetCheck( - sides, normal, 0, point, 0, 0, orient); - result(index, 1) = VertexCFD::GeometryPrimitives::planeIntersect( - sides, normal, 0, point, 0, 0); - result(index, 2) = VertexCFD::GeometryPrimitives::distanceToTriangleFace( - sides, normal, 0, point, 0, 0, orient); -} - -template -void testEval() -{ - double tol = 1e-6; - - // Define kokkos views needed for all tests - Kokkos::View point( - Kokkos::ViewAllocateWithoutInitializing("pointView"), 1, 1, 3); - Kokkos::View sides( - Kokkos::ViewAllocateWithoutInitializing("sidesView"), 1, 3, 3); - Kokkos::View normal( - Kokkos::ViewAllocateWithoutInitializing("normalView"), 1, 3); - Kokkos::View dist( - Kokkos::ViewAllocateWithoutInitializing("distanceView")); - - // Test of linearEdge function - Kokkos::parallel_for( - "linearEdgeTest", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - // Define the line segment to test against - for (int dim = 0; dim < 3; ++dim) - { - sides(0, 0, dim) = 0.0; - sides(0, 1, dim) = 1.0; - } - sides(0, 1, 2) = 0.0; - - // Test for 3d test_point that is closest to some point on the line - // segment - point(0, 0, 0) = 0.1; - point(0, 0, 1) = 0.1; - point(0, 0, 2) = 1.0; - int index[2] = {0, 1}; - dist(0) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 3, index); - - // Test for 3d test_point that is closest to the end of the line - point(0, 0, 0) = 2.5; - point(0, 0, 1) = 1.0; - point(0, 0, 2) = 2.0; - dist(1) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 3, index); - - // Test for 3d test_point that is closest to the start of the line - point(0, 0, 0) = -1.0; - point(0, 0, 1) = -2.4; - point(0, 0, 2) = 0.0; - dist(2) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 3, index); - - // Test for 2d test_point that is closest to the end of the line - point(0, 0, 0) = 1.0; - point(0, 0, 1) = 3.0; - dist(3) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 2, index); - - // Test for 2d test_point that is closest to the some point on the - // line - point(0, 0, 0) = 0.2; - point(0, 0, 1) = 0.8; - dist(4) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 2, index); - - // Test for 2d test_point that is closest to the start of the line - point(0, 0, 0) = -0.8; - point(0, 0, 1) = 0.6; - dist(5) = VertexCFD::GeometryPrimitives::distanceToLinearEdge( - sides, 0, point, 0, 0, 2, index); - }); - - auto dist_mirror - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), dist); - EXPECT_DOUBLE_EQ(1.0, dist_mirror(0)); - EXPECT_DOUBLE_EQ(2.5, dist_mirror(1)); - EXPECT_DOUBLE_EQ(2.6, dist_mirror(2)); - EXPECT_DOUBLE_EQ(2.0, dist_mirror(3)); - EXPECT_NEAR(0.424264, dist_mirror(4), tol); - EXPECT_DOUBLE_EQ(1.0, dist_mirror(5)); - - Kokkos::View result( - Kokkos::ViewAllocateWithoutInitializing("ResultView")); - - // Test of distanceToTriangleFace, tetCheck and planeIntersect functions - Kokkos::parallel_for( - "tetIntersectTest", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - // Define the unit triangle to test against - for (int dim = 0; dim < 3; ++dim) - { - sides(0, 0, dim) = 0.0; - sides(0, 1, dim) = 0.0; - sides(0, 2, dim) = 0.0; - normal(0, dim) = 0.0; - } - sides(0, 1, 0) = 1.0; - sides(0, 2, 1) = 1.0; - normal(0, 2) = 1.0; - - // Test for a point that intersects the triangle - int index = 0; - point(0, 0, 0) = 0.1; - point(0, 0, 1) = 0.1; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge ab - ++index; - point(0, 0, 0) = 0.5; - point(0, 0, 1) = -0.5; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge bc - ++index; - point(0, 0, 0) = 1.0; - point(0, 0, 1) = 1.0; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge ca - ++index; - point(0, 0, 0) = -0.5; - point(0, 0, 1) = 0.5; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge ab or bc - ++index; - point(0, 0, 0) = 2.0; - point(0, 0, 1) = -1.0; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge bc or ca - ++index; - point(0, 0, 0) = -1.0; - point(0, 0, 1) = 4.0; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - - // Test for a point that is closest to edge ca or ab - ++index; - point(0, 0, 0) = -2.0; - point(0, 0, 1) = -1.0; - point(0, 0, 2) = 1.0; - faceDistanceFunc(sides, point, result, normal, index); - }); - - auto result_mirror - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), result); - - EXPECT_EQ(0, result_mirror(0, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(0, 1)); - EXPECT_NEAR(1.0, result_mirror(0, 2), tol); - - EXPECT_EQ(1, result_mirror(1, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(1, 1)); - EXPECT_NEAR(1.118034, result_mirror(1, 2), tol); - - EXPECT_EQ(2, result_mirror(2, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(2, 1)); - EXPECT_NEAR(1.22474487, result_mirror(2, 2), tol); - - EXPECT_EQ(3, result_mirror(3, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(3, 1)); - EXPECT_NEAR(1.118034, result_mirror(3, 2), tol); - - EXPECT_EQ(4, result_mirror(4, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(4, 1)); - EXPECT_NEAR(1.732050807, result_mirror(4, 2), tol); - - EXPECT_EQ(5, result_mirror(5, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(5, 1)); - EXPECT_NEAR(3.31662479, result_mirror(5, 2), tol); - - EXPECT_EQ(6, result_mirror(6, 0)); - EXPECT_DOUBLE_EQ(1.0, result_mirror(6, 1)); - EXPECT_NEAR(2.44948974, result_mirror(6, 2), tol); - - // Test the crossProduct function - Kokkos::View cross_product( - Kokkos::ViewAllocateWithoutInitializing("CrossProductView")); - - Kokkos::parallel_for( - "CrossProductTest", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - double array1[3]; - double array2[3]; - double array_out[3]; - array1[0] = 1.0; - array1[1] = 0.0; - array1[2] = 0.0; - - array2[0] = 0.0; - array2[1] = 1.0; - array2[2] = 0.0; - VertexCFD::GeometryPrimitives::crossProduct( - array1, array2, array_out); - cross_product(0, 0) = array_out[0]; - cross_product(0, 1) = array_out[1]; - cross_product(0, 2) = array_out[2]; - - array2[0] = 0.0; - array2[1] = 0.0; - array2[2] = 1.0; - VertexCFD::GeometryPrimitives::crossProduct( - array1, array2, array_out); - cross_product(1, 0) = array_out[0]; - cross_product(1, 1) = array_out[1]; - cross_product(1, 2) = array_out[2]; - - array2[0] = 1.0; - array2[1] = 0.0; - array2[2] = 0.0; - VertexCFD::GeometryPrimitives::crossProduct( - array1, array2, array_out); - cross_product(2, 0) = array_out[0]; - cross_product(2, 1) = array_out[1]; - cross_product(2, 2) = array_out[2]; - - array2[0] = 1.0; - array2[1] = 1.0; - array2[2] = 1.0; - VertexCFD::GeometryPrimitives::crossProduct( - array1, array2, array_out); - cross_product(3, 0) = array_out[0]; - cross_product(3, 1) = array_out[1]; - cross_product(3, 2) = array_out[2]; - - array2[0] = 0.0; - array2[1] = 0.0; - array2[2] = 0.0; - VertexCFD::GeometryPrimitives::crossProduct( - array1, array2, array_out); - cross_product(4, 0) = array_out[0]; - cross_product(4, 1) = array_out[1]; - cross_product(4, 2) = array_out[2]; - }); - - auto cross_product_mirror = Kokkos::create_mirror_view_and_copy( - Kokkos::HostSpace(), cross_product); - EXPECT_EQ(0, cross_product_mirror(0, 0)); - EXPECT_EQ(0, cross_product_mirror(0, 1)); - EXPECT_EQ(1, cross_product_mirror(0, 2)); - - EXPECT_EQ(0, cross_product_mirror(1, 0)); - EXPECT_EQ(-1, cross_product_mirror(1, 1)); - EXPECT_EQ(0, cross_product_mirror(1, 2)); - - EXPECT_EQ(0, cross_product_mirror(2, 0)); - EXPECT_EQ(0, cross_product_mirror(2, 1)); - EXPECT_EQ(0, cross_product_mirror(2, 2)); - - EXPECT_EQ(0, cross_product_mirror(3, 0)); - EXPECT_EQ(-1, cross_product_mirror(3, 1)); - EXPECT_EQ(1, cross_product_mirror(3, 2)); - - EXPECT_EQ(0, cross_product_mirror(4, 0)); - EXPECT_EQ(0, cross_product_mirror(4, 1)); - EXPECT_EQ(0, cross_product_mirror(4, 2)); -} - -//---------------------------------------------------------------------------// -TEST(GeometryPrimitives, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(GeometryPrimitives, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/mesh/unit_test/tstRestart.cpp b/src/mesh/unit_test/tstRestart.cpp deleted file mode 100644 index fc185f5..0000000 --- a/src/mesh/unit_test/tstRestart.cpp +++ /dev/null @@ -1,374 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -struct Fixture -{ - Teuchos::RCP> _comm; - Teuchos::RCP _mesh; - Teuchos::RCP _dof_manager; - Teuchos::RCP> _x; - Teuchos::RCP> _x_dot; - - Fixture(const std::string& lin_alg_type, const bool with_periodic_bc) - { - // Create mesh. - auto mesh_factory - = Teuchos::rcp(new panzer_stk::SquareQuadMeshFactory()); - auto mesh_params = Teuchos::parameterList(); - mesh_params->set("X Procs", -1); - mesh_params->set("Y Procs", -1); - mesh_params->set("X0", 0.0); - mesh_params->set("Y0", 0.0); - mesh_params->set("Xf", 1.0); - mesh_params->set("Yf", 1.0); - mesh_params->set("X Elements", 25); - mesh_params->set("Y Elements", 25); - if (with_periodic_bc) - { - mesh_params->set("X Blocks", 1); - mesh_params->set("Y Blocks", 1); - mesh_params->sublist("Periodic BCs"); - mesh_params->sublist("Periodic BCs").set("Count", 2); - mesh_params->sublist("Periodic BCs") - .set("Periodic Condition 1", - "x-all 1e-8: top;bottom"); - mesh_params->sublist("Periodic BCs") - .set("Periodic Condition 2", - "y-all 1e-8: left;right"); - } - mesh_factory->setParameterList(mesh_params); - _mesh = mesh_factory->buildUncommitedMesh(MPI_COMM_WORLD); - mesh_factory->completeMeshConstruction(*_mesh, MPI_COMM_WORLD); - - // Create dof manager. - auto conn_manager = Teuchos::rcp(new panzer_stk::STKConnManager(_mesh)); - _dof_manager = Teuchos::rcp( - new panzer::DOFManager(conn_manager, MPI_COMM_WORLD)); - shards::CellTopology cell_topo( - shards::getCellTopologyData>()); - auto field_pattern - = Teuchos::rcp(new panzer::NodalFieldPattern(cell_topo)); - _dof_manager->addField("eblock-0_0", "test_field", field_pattern); - _dof_manager->buildGlobalUnknowns(); - _comm = Teuchos::rcp_dynamic_cast>( - _dof_manager->getComm()); - - // Create state vector and state vector time derivative. - Teuchos::RCP> - linear_object_factory; - Teuchos::RCP> space; - if (lin_alg_type == "Epetra") - { - auto epetra_factory = Teuchos::rcp( - new panzer::BlockedEpetraLinearObjFactory( - _comm, _dof_manager)); - space = epetra_factory->getThyraDomainSpace(); - linear_object_factory = epetra_factory; - } - else if (lin_alg_type == "Tpetra") - { - auto tpetra_factory = Teuchos::rcp( - new panzer::TpetraLinearObjFactory( - _comm, _dof_manager)); - space = tpetra_factory->getThyraDomainSpace(); - linear_object_factory = tpetra_factory; - } - else - { - throw std::logic_error("Invalid linear algebra type"); - } - _x = Thyra::createMember(*space); - _x_dot = Thyra::createMember(*space); - - // Get local data views - auto x_spmd - = Teuchos::rcp_dynamic_cast>(_x); - auto spmd_space = x_spmd->spmdSpace(); - auto local_size = spmd_space->localSubDim(); - - // Initialize max double value - std::vector x_values(local_size, - std::numeric_limits::max()); - std::vector x_dot_values(local_size, - std::numeric_limits::max()); - - // Compute global-to-local mapping - std::unordered_map global_to_local; - std::vector gids; - _dof_manager->getOwnedIndices(gids); - for (int i = 0; i < local_size; ++i) - global_to_local[gids[i]] = i; - - // Load the x coordinate as the solution and load the y coordinate - // as the solution time derivative. - const auto& coord_field = _mesh->getCoordinatesField(); - const auto& local_elems = *(_mesh->getElementsOrderedByLID()); - const int num_local_elem = local_elems.size(); - std::vector owned_elems; - _mesh->getMyElements(owned_elems); - const int num_own_elem = owned_elems.size(); - std::vector owned_elem_ids(num_own_elem); - for (int i = 0; i < num_own_elem; ++i) - { - owned_elem_ids[i] = _mesh->elementGlobalId(owned_elems[i]); - } - std::sort(owned_elem_ids.begin(), owned_elem_ids.end()); - std::vector elem_dofs; - for (int i = 0; i < num_local_elem; ++i) - { - stk::mesh::EntityId elem_id - = _mesh->elementGlobalId(local_elems[i]); - const bool is_local = std::binary_search( - owned_elem_ids.begin(), owned_elem_ids.end(), elem_id); - if (is_local) - { - _dof_manager->getElementGIDs(i, elem_dofs); - const int elem_num_dof = elem_dofs.size(); - const stk::mesh::Entity* elem_nodes - = _mesh->getBulkData()->begin_nodes(local_elems[i]); - for (int d = 0; d < elem_num_dof; ++d) - { - const double* node_coords - = stk::mesh::field_data(coord_field, elem_nodes[d]); - auto itr = global_to_local.find(elem_dofs[d]); - - // Some elements may have combination of owned and - // non-owned DOFs Need to check for existence here - if (itr != global_to_local.end()) - { - int dof_lid = itr->second; - - // For the periodic case, multiple coordinates map to a - // single DoF. Using min here ensures a unique solution - // at each DoF. - x_values[dof_lid] - = std::min(x_values[dof_lid], node_coords[0]); - x_dot_values[dof_lid] - = std::min(x_dot_values[dof_lid], node_coords[1]); - } - } - } - } - this->update_vector(_x, x_values); - this->update_vector(_x_dot, x_dot_values); - _comm->barrier(); - } - - //---------------------------------------------------------------------------// - void update_vector(const Teuchos::RCP>& vec, - const std::vector& values) const - { - // Vector should be either a DefaultSpmdVector (Epetra) or a - // TpetraVector - auto spmd_vec - = Teuchos::rcp_dynamic_cast>(vec); - auto thyratpetra_vec = Teuchos::rcp_dynamic_cast< - Thyra::TpetraVector>( - vec); - - if (spmd_vec == Teuchos::null && thyratpetra_vec == Teuchos::null) - throw std::runtime_error("Unrecognized Thyra vector type"); - - if (spmd_vec != Teuchos::null) - { - auto space = vec->space(); - auto epetra_map = Thyra::get_Epetra_Map(space); - auto epetra_vec = Thyra::get_Epetra_Vector(vec, epetra_map); - std::copy(values.begin(), values.end(), epetra_vec->Values()); - } - else - { - auto tpetra_vec = thyratpetra_vec->getTpetraVector(); - auto data_view = tpetra_vec->getLocalViewHost( - Tpetra::Access::OverwriteAllStruct()); - std::copy(values.begin(), values.end(), data_view.data()); - } - } -}; - -//---------------------------------------------------------------------------// -void testReadOnly(const std::string& lin_alg_type, const bool with_periodic_bc) -{ - // Create test fixture. - Fixture fix(lin_alg_type, with_periodic_bc); - - // Read the file we have stored. Put some garbage in the new vectors to - // make sure they are overwritten. - auto new_x = fix._x->clone_v(); - auto new_x_dot = fix._x_dot->clone_v(); - Thyra::assign(new_x.ptr(), -1394932.39); - Thyra::assign(new_x_dot.ptr(), 432.3); - Teuchos::ParameterList input_params; - if (!with_periodic_bc) - { - input_params.set("Restart Data File Name", - "read_only_test.restart.data"); - input_params.set("Restart DOF Map File Name", - "read_only_test.restart.dofmap"); - } - else - { - input_params.set("Restart Data File Name", - "read_only_test_periodic.restart.data"); - input_params.set("Restart DOF Map File Name", - "read_only_test_periodic.restart.dofmap"); - } - Mesh::RestartReader reader(fix._comm, input_params); - EXPECT_EQ(1.49, reader.initialStateTime()); - reader.readSolution(fix._mesh, fix._dof_manager, new_x, new_x_dot); - - // Check the results. - Thyra::Vp_V(new_x.ptr(), *(fix._x), -1.0); - auto x_norm = Thyra::norm_2(*new_x); - EXPECT_EQ(x_norm, 0.0); - - Thyra::Vp_V(new_x_dot.ptr(), *(fix._x_dot), -1.0); - auto x_dot_norm = Thyra::norm_2(*new_x_dot); - EXPECT_EQ(x_dot_norm, 0.0); -} - -//---------------------------------------------------------------------------// -void testWriteRead(const std::string& lin_alg_type, const bool with_periodic_bc) -{ - // Create test fixture. - Fixture fix(lin_alg_type, with_periodic_bc); - - // Ordinarily, an exception is thrown if we attempt to overwrite an - // existing DOF Map file. This will allow overwriting to avoid exceptions - // from repeated test execution. - constexpr bool allow_dofmap_overwrite = true; - - // Write the file. - Teuchos::ParameterList output_params; - if (!with_periodic_bc) - output_params.set("Restart File Prefix", "restart_test"); - else - output_params.set("Restart File Prefix", "restart_test_periodic"); - Mesh::RestartWriter writer( - fix._mesh, fix._dof_manager, output_params, allow_dofmap_overwrite); - writer.writeSolution(fix._x, fix._x_dot, 12, 1.49); - - // Read the file back in. Put some garbage in the new vectors to make sure - // they are overwritten. - auto new_x = fix._x->clone_v(); - auto new_x_dot = fix._x_dot->clone_v(); - Thyra::assign(new_x.ptr(), -1394932.39); - Thyra::assign(new_x_dot.ptr(), 432.3); - Teuchos::ParameterList input_params; - if (!with_periodic_bc) - { - input_params.set("Restart Data File Name", - "restart_test_12.restart.data"); - input_params.set("Restart DOF Map File Name", - "restart_test.restart.dofmap"); - } - else - { - input_params.set("Restart Data File Name", - "restart_test_periodic_12.restart.data"); - input_params.set("Restart DOF Map File Name", - "restart_test_periodic.restart.dofmap"); - } - Mesh::RestartReader reader(fix._comm, input_params); - EXPECT_EQ(1.49, reader.initialStateTime()); - reader.readSolution(fix._mesh, fix._dof_manager, new_x, new_x_dot); - - // Check the results. - Thyra::Vp_V(new_x.ptr(), *(fix._x), -1.0); - auto x_norm = Thyra::norm_2(*new_x); - EXPECT_EQ(x_norm, 0.0); - - Thyra::Vp_V(new_x_dot.ptr(), *(fix._x_dot), -1.0); - auto x_dot_norm = Thyra::norm_2(*new_x_dot); - EXPECT_EQ(x_dot_norm, 0.0); -} - -//---------------------------------------------------------------------------// -TEST(RestartReaderEpetra, restart_read_only_test) -{ - testReadOnly("Epetra", false); -} - -//---------------------------------------------------------------------------// -TEST(RestartReaderTpetra, restart_read_only_test) -{ - testReadOnly("Tpetra", false); -} - -//---------------------------------------------------------------------------// -TEST(RestartWriterEpetra, restart_write_read_test) -{ - testWriteRead("Epetra", false); -} - -//---------------------------------------------------------------------------// -TEST(RestartWriterTpetra, restart_write_read_test) -{ - testWriteRead("Tpetra", false); -} - -//---------------------------------------------------------------------------// -TEST(RestartReaderEpetra, periodic_restart_read_only_test) -{ - testReadOnly("Epetra", true); -} - -//---------------------------------------------------------------------------// -TEST(RestartReaderTpetra, periodic_restart_read_only_test) -{ - testReadOnly("Tpetra", true); -} - -//---------------------------------------------------------------------------// -TEST(RestartWriterEpetra, periodic_restart_write_read_test) -{ - testWriteRead("Epetra", true); -} - -//---------------------------------------------------------------------------// -TEST(RestartWriterTpetra, periodic_restart_write_read_test) -{ - testWriteRead("Tpetra", true); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/observers/VertexCFD_Compute_ErrorNorms.hpp b/src/observers/VertexCFD_Compute_ErrorNorms.hpp deleted file mode 100644 index 9d76dec..0000000 --- a/src/observers/VertexCFD_Compute_ErrorNorms.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef VERTEXCFD_COMPUTE_ERRORNORMS_HPP -#define VERTEXCFD_COMPUTE_ERRORNORMS_HPP - -#include "boundary_conditions/VertexCFD_BCStrategy_Factory.hpp" -#include "equation_sets/VertexCFD_EquationSet_Factory.hpp" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ComputeErrorNorms -{ -//---------------------------------------------------------------------------// -template -class ErrorNorms -{ - public: - ErrorNorms( - const Teuchos::RCP& mesh, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& - response_library, - const std::vector>& physics_blocks, - const panzer::ClosureModelFactory_TemplateManager& - cm_factory, - const Teuchos::ParameterList& closure_params, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP& eq_set_factory, - const double volume, - const int integration_order); - - void ComputeNorms( - const Teuchos::RCP>& working_state); - - struct DofErrorNorm - { - std::string name{}; - double error_norm = 0.0; - - explicit DofErrorNorm(const std::string& dof_name) - : name(dof_name) - { - } - ~DofErrorNorm() = default; - DofErrorNorm(const DofErrorNorm&) = default; - DofErrorNorm& operator=(const DofErrorNorm&) = default; - DofErrorNorm(DofErrorNorm&&) = default; - DofErrorNorm& operator=(DofErrorNorm&&) = default; - }; - - private: - Teuchos::RCP> _lof; - Teuchos::RCP> _response_library; - std::vector> _physics_blocks; - panzer::ClosureModelFactory_TemplateManager _cm_factory; - Teuchos::ParameterList _closure_params; - Teuchos::ParameterList _user_params; - - Teuchos::RCP _eq_set_factory; - - double _volume; - std::vector _L1_error_norms; - std::vector _L2_error_norms; - int _L1_error_norm_order; - int _L2_error_norm_order; - int _num_mom_eq; - - public: - const std::vector& L1_errorNorms() const - { - return _L1_error_norms; - } - - const std::vector& L2_errorNorms() const - { - return _L2_error_norms; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace ComputeErrorNorms -} // end namespace VertexCFD - -#include "VertexCFD_Compute_ErrorNorms_impl.hpp" - -#endif // end VERTEXCFD_COMPUTE_ERRORNORMS_HPP diff --git a/src/observers/VertexCFD_Compute_ErrorNorms_impl.hpp b/src/observers/VertexCFD_Compute_ErrorNorms_impl.hpp deleted file mode 100644 index 1fb0ac7..0000000 --- a/src/observers/VertexCFD_Compute_ErrorNorms_impl.hpp +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef VERTEXCFD_COMPUTE_ERRORNORMS_IMPL_HPP -#define VERTEXCFD_COMPUTE_ERRORNORMS_IMPL_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ComputeErrorNorms -{ -//---------------------------------------------------------------------------// -template -ErrorNorms::ErrorNorms( - const Teuchos::RCP& mesh, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& response_library, - const std::vector>& physics_blocks, - const panzer::ClosureModelFactory_TemplateManager& cm_factory, - const Teuchos::ParameterList& closure_params, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP& eq_set_factory, - const double volume, - const int integration_order) - : _lof(lof) - , _response_library(response_library) - , _physics_blocks(physics_blocks) - , _cm_factory(cm_factory) - , _closure_params(closure_params) - , _user_params(user_params) - , _eq_set_factory(eq_set_factory) - , _volume(volume) -{ - // Initialize names of equation based on the equation set name - std::vector eq_name; - _num_mom_eq = mesh->getDimension(); - // Continuity equation - eq_name.push_back("continuity"); - // Temperature equation - if (_user_params.isType("Build Temperature Equation")) - { - if (_user_params.get("Build Temperature Equation")) - eq_name.push_back("energy"); - } - // Full induction equation - if (_user_params.isSublist("Full Induction MHD Properties")) - { - for (int i = 0; i < _num_mom_eq; ++i) - { - eq_name.push_back("induction_" + std::to_string(i)); - } - } - // Momentum equation - for (int i = 0; i < _num_mom_eq; ++i) - eq_name.push_back("momentum_" + std::to_string(i)); - - // Add response library - std::vector element_blocks; - mesh->getElementBlockNames(element_blocks); - - panzer::FunctionalResponse_Builder response_builder; - response_builder.comm = Teuchos::getRawMpiComm(*(mesh->getComm())); - response_builder.cubatureDegree = integration_order; - response_builder.requiresCellIntegral = true; - - // L1 error norm - auto add_dof_L1 = [&](const std::string& dof) { - response_builder.quadPointField = "L1_Error_" + dof; - _response_library->addResponse( - dof + "_L1_error_norms", element_blocks, response_builder); - - _L1_error_norms.emplace_back(dof); - }; - - // L2 error norm - auto add_dof_L2 = [&](const std::string& dof) { - response_builder.quadPointField = "L2_Error_" + dof; - _response_library->addResponse( - dof + "_L2_error_norms", element_blocks, response_builder); - - _L2_error_norms.emplace_back(dof); - }; - - // Add L1 and L2 error norms - for (auto& element : eq_name) - { - add_dof_L1(element); - add_dof_L2(element); - } - - // Finalize construction of response library - _response_library->buildResponseEvaluators( - _physics_blocks, _cm_factory, _closure_params, _user_params); -} - -//---------------------------------------------------------------------------// -template -void ErrorNorms::ComputeNorms( - const Teuchos::RCP>& working_state) -{ - // Get the working X - auto x = working_state->getX(); - - // Assemble linear system - panzer::AssemblyEngineInArgs in_args; - in_args.container_ = _lof->buildLinearObjContainer(); - in_args.ghostedContainer_ = _lof->buildGhostedLinearObjContainer(); - in_args.evaluate_transient_terms = false; - in_args.time = working_state->getTime(); - - _lof->initializeGhostedContainer(panzer::LinearObjContainer::X, - *(in_args.ghostedContainer_)); - - auto thyra_container - = Teuchos::rcp_dynamic_cast>( - in_args.container_); - thyra_container->set_x_th( - Teuchos::rcp_const_cast>(x)); - - // Set up resp, resp_func, resp_vec for L1_error_norms - for (auto& dof : _L1_error_norms) - { - auto resp = _response_library->getResponse( - dof.name + "_L1_error_norms"); - auto resp_func = Teuchos::rcp_dynamic_cast< - panzer::Response_Functional>(resp); - auto resp_vec = Thyra::createMember(resp_func->getVectorSpace()); - resp_func->setVector(resp_vec); - } - - // Set up resp, resp_func, resp_vec for L2_error_norms - for (auto& dof : _L2_error_norms) - { - auto resp = _response_library->getResponse( - dof.name + "_L2_error_norms"); - auto resp_func = Teuchos::rcp_dynamic_cast< - panzer::Response_Functional>(resp); - auto resp_vec = Thyra::createMember(resp_func->getVectorSpace()); - resp_func->setVector(resp_vec); - } - - _response_library->addResponsesToInArgs(in_args); - _response_library->evaluate(in_args); - - // Compute L1 error norm - for (auto& dof : _L1_error_norms) - { - auto resp = _response_library->getResponse( - dof.name + "_L1_error_norms"); - auto resp_func = Teuchos::rcp_dynamic_cast< - panzer::Response_Functional>(resp); - dof.error_norm = (resp_func->value) / _volume; - } - - // Compute L2 error norm - for (auto& dof : _L2_error_norms) - { - auto resp = _response_library->getResponse( - dof.name + "_L2_error_norms"); - auto resp_func = Teuchos::rcp_dynamic_cast< - panzer::Response_Functional>(resp); - dof.error_norm = std::sqrt(resp_func->value) / _volume; - } -} - -//---------------------------------------------------------------------------// - -} // end namespace ComputeErrorNorms -} // end namespace VertexCFD - -#endif // end VERTEXCFD_COMPUTE_ERRORNORMS_IMPL_HPP diff --git a/src/observers/VertexCFD_Compute_Volume.hpp b/src/observers/VertexCFD_Compute_Volume.hpp deleted file mode 100644 index 5ca2d2d..0000000 --- a/src/observers/VertexCFD_Compute_Volume.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef VERTEXCFD_COMPUTE_VOLUME_HPP -#define VERTEXCFD_COMPUTE_VOLUME_HPP - -#include "equation_sets/VertexCFD_EquationSet_Factory.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace ComputeVolume -{ -//---------------------------------------------------------------------------// -template -class Volume -{ - public: - Volume( - const Teuchos::RCP& mesh, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& - response_library, - const std::vector>& physics_blocks, - const panzer::ClosureModelFactory_TemplateManager& - cm_factory, - const Teuchos::ParameterList& closure_params, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP& workset_container, - const std::vector& bcs, - const Teuchos::RCP& bc_factory, - const Teuchos::RCP& eq_set_factory, - const int integration_order); - - void ComputeVol(); - double volume() const; - - private: - Teuchos::RCP> _lof; - Teuchos::RCP> _response_library; - std::vector> _physics_blocks; - panzer::ClosureModelFactory_TemplateManager _cm_factory; - Teuchos::ParameterList _closure_params; - Teuchos::ParameterList _user_params; - Teuchos::RCP _workset_container; - - std::vector _bcs; - Teuchos::RCP _bc_factory; - Teuchos::RCP _eq_set_factory; - - double _volume; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ComputeVolume -} // end namespace VertexCFD - -#include "VertexCFD_Compute_Volume_impl.hpp" - -#endif // end VERTEXCFD_COMPUTE_VOLUME_HPP diff --git a/src/observers/VertexCFD_Compute_Volume_impl.hpp b/src/observers/VertexCFD_Compute_Volume_impl.hpp deleted file mode 100644 index dac281a..0000000 --- a/src/observers/VertexCFD_Compute_Volume_impl.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef VERTEXCFD_COMPUTE_VOLUME_IMPL_HPP -#define VERTEXCFD_COMPUTE_VOLUME_IMPL_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ComputeVolume -{ -//---------------------------------------------------------------------------// -template -Volume::Volume( - const Teuchos::RCP& mesh, - const Teuchos::RCP>& lof, - const Teuchos::RCP>& response_library, - const std::vector>& physics_blocks, - const panzer::ClosureModelFactory_TemplateManager& cm_factory, - const Teuchos::ParameterList& closure_params, - const Teuchos::ParameterList& user_params, - const Teuchos::RCP& workset_container, - const std::vector& bcs, - const Teuchos::RCP& bc_factory, - const Teuchos::RCP& eq_set_factory, - const int integration_order) - : _lof(lof) - , _response_library(response_library) - , _physics_blocks(physics_blocks) - , _cm_factory(cm_factory) - , _closure_params(closure_params) - , _user_params(user_params) - , _workset_container(workset_container) - , _bcs(bcs) - , _bc_factory(bc_factory) - , _eq_set_factory(eq_set_factory) -{ - // Add response library - std::vector element_blocks; - mesh->getElementBlockNames(element_blocks); - - panzer::FunctionalResponse_Builder response_builder_vol; - response_builder_vol.comm = Teuchos::getRawMpiComm(*(mesh->getComm())); - response_builder_vol.cubatureDegree = integration_order; - response_builder_vol.requiresCellIntegral = true; - - response_builder_vol.quadPointField = "volume"; - - _response_library->addResponse( - "compute volume", element_blocks, response_builder_vol); - - // Finalize construction of response library - _response_library->buildResponseEvaluators( - _physics_blocks, _cm_factory, _closure_params, _user_params); -} - -//---------------------------------------------------------------------------// -template -void Volume::ComputeVol() -{ - // Assemble linear system - panzer::AssemblyEngineInArgs in_args; - in_args.container_ = _lof->buildLinearObjContainer(); - in_args.ghostedContainer_ = _lof->buildGhostedLinearObjContainer(); - in_args.evaluate_transient_terms = false; - - _lof->initializeGhostedContainer(panzer::LinearObjContainer::X - | panzer::LinearObjContainer::F - | panzer::LinearObjContainer::Mat, - *(in_args.ghostedContainer_)); - - // Set up resp, resp_func, resp_vec for volume - auto resp_vol = _response_library->getResponse( - "compute volume"); - auto resp_func_vol = Teuchos::rcp_dynamic_cast< - panzer::Response_Functional>(resp_vol); - auto resp_vec_vol = Thyra::createMember(resp_func_vol->getVectorSpace()); - resp_func_vol->setVector(resp_vec_vol); - - _response_library->addResponsesToInArgs(in_args); - _response_library->evaluate(in_args); - - _volume = resp_func_vol->value; -} - -//---------------------------------------------------------------------------// -template -double Volume::volume() const -{ - return _volume; -} -//---------------------------------------------------------------------------// - -} // end namespace ComputeVolume -} // end namespace VertexCFD - -#endif // end VERTEXCFD_COMPUTE_VOLUME_IMPL_HPP diff --git a/src/observers/VertexCFD_NOXObserver_IterationOutput.cpp b/src/observers/VertexCFD_NOXObserver_IterationOutput.cpp deleted file mode 100644 index bfc9cc9..0000000 --- a/src/observers/VertexCFD_NOXObserver_IterationOutput.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "VertexCFD_NOXObserver_IterationOutput.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace NOXObserver -{ -//---------------------------------------------------------------------------// -IterationOutput::IterationOutput() - : _ostream(Teuchos::rcp(&std::cout, false)) -{ - _ostream.setShowProcRank(false); - _ostream.setOutputToRootOnly(0); -} - -//---------------------------------------------------------------------------// -void IterationOutput::runPreIterate(const NOX::Solver::Generic&) {} - -//---------------------------------------------------------------------------// -void IterationOutput::runPostIterate(const NOX::Solver::Generic& solver) -{ - // Output nonlinear solver results after nonlinear iteration. - auto stats = solver.getSolverStatistics(); - const auto& group = solver.getSolutionGroup(); - const SolveDataExtractor& data_extractor - = dynamic_cast(group); - _ostream << " " << std::setw(9) << std::right << std::fixed - << stats->numNonlinearIterations; - _ostream << " " << std::setw(8) << std::left << std::setprecision(2) - << std::scientific << group.getNormF(); - _ostream << " " << std::setw(8) << std::right << std::fixed - << data_extractor.lastLinearSolveNumIters(); - _ostream << " " << std::setw(8) << std::left << std::setprecision(2) - << std::scientific << data_extractor.lastLinearSolveAchievedTol(); - _ostream << " \n"; -} - -//---------------------------------------------------------------------------// -void IterationOutput::runPreSolve(const NOX::Solver::Generic& solver) -{ - _ostream << " | "; - _ostream << std::setw(9) << std::right << "Nonlinear"; - _ostream << " | " << std::setw(8) << std::left << "F 2-Norm"; - _ostream << " | "; - _ostream << std::setw(8) << std::right << "# Linear"; - _ostream << " | " << std::setw(8) << std::left << "R 2-Norm"; - _ostream << " | \n"; - - const auto& group = solver.getSolutionGroup(); - if (!group.isF()) - { - const_cast(group).computeF(); - } - _ostream << " " << std::setw(9) << std::right << std::fixed << 0; - _ostream << " " << std::setw(8) << std::left << std::setprecision(2) - << std::scientific << group.getNormF() << "\n"; -} - -//---------------------------------------------------------------------------// -void IterationOutput::runPostSolve(const NOX::Solver::Generic&) {} - -//---------------------------------------------------------------------------// -SolveDataExtractor::SolveDataExtractor(const NOX::Thyra::Group& group) - : NOX::Thyra::Group(group, NOX::ShapeCopy) -{ -} - -//---------------------------------------------------------------------------// -int SolveDataExtractor::lastLinearSolveNumIters() const -{ - return this->last_linear_solve_num_iters_; -} - -//---------------------------------------------------------------------------// -double SolveDataExtractor::lastLinearSolveAchievedTol() const -{ - return this->last_linear_solve_achieved_tol_; -} - -//---------------------------------------------------------------------------// - -} // end namespace NOXObserver -} // end namespace VertexCFD diff --git a/src/observers/VertexCFD_NOXObserver_IterationOutput.hpp b/src/observers/VertexCFD_NOXObserver_IterationOutput.hpp deleted file mode 100644 index 83d8c06..0000000 --- a/src/observers/VertexCFD_NOXObserver_IterationOutput.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef VERTEXCFD_NOXOBSERVER_ITERATIONOUTPUT_HPP -#define VERTEXCFD_NOXOBSERVER_ITERATIONOUTPUT_HPP - -#include -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace NOXObserver -{ -//---------------------------------------------------------------------------// -class IterationOutput : public NOX::Observer -{ - public: - IterationOutput(); - - //! User defined method that will be executed at the start of a call to - //! NOX::Solver::Generic::step(). - void runPreIterate(const NOX::Solver::Generic& solver) override; - - //! User defined method that will be executed at the end of a call to - //! NOX::Solver::Generic::step(). - void runPostIterate(const NOX::Solver::Generic& solver) override; - - //! User defined method that will be executed at the start of a call to - //! NOX::Solver::Generic::solve(). - void runPreSolve(const NOX::Solver::Generic& solver) override; - - //! User defined method that will be executed at the end of a call to - //! NOX::Solver::Generic::solve(). - void runPostSolve(const NOX::Solver::Generic& solver) override; - - private: - Teuchos::FancyOStream _ostream; -}; - -//---------------------------------------------------------------------------// -// This class is a workaround for the unused lineaer solver status in -// NOX::SolverStats::LinearSolveStats::LogLinearSolve() -class SolveDataExtractor : public NOX::Thyra::Group -{ - public: - SolveDataExtractor(const NOX::Thyra::Group& group); - int lastLinearSolveNumIters() const; - double lastLinearSolveAchievedTol() const; -}; - -//---------------------------------------------------------------------------// - -} // end namespace NOXObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_NOXOBSERVER_ITERATIONOUTPUT_HPP diff --git a/src/observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp b/src/observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp deleted file mode 100644 index 485fbda..0000000 --- a/src/observers/VertexCFD_TempusObserver_ErrorNormOutput.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_HPP -#define VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_HPP - -#include - -#include -#include - -#include -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class ErrorNormOutput : virtual public Tempus::IntegratorObserver -{ - public: - ErrorNormOutput( - const Teuchos::ParameterList& error_norm_list, - Teuchos::RCP> error_norm); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - /// Print error norms with heading - void - print_error_norms(const std::string& heading, - const std::vector::DofErrorNorm>& error_norm, - const double scaling = 1.0); - - int _output_freq; - bool _compute_time_error; - Teuchos::FancyOStream _ostream; - Teuchos::RCP> _error_norm; - static constexpr int prec = std::numeric_limits::digits10 + 1; - std::vector::DofErrorNorm> - _L1_time_error_norms; - std::vector::DofErrorNorm> - _L2_time_error_norms; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#include "VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_HPP diff --git a/src/observers/VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp b/src/observers/VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp deleted file mode 100644 index 3912133..0000000 --- a/src/observers/VertexCFD_TempusObserver_ErrorNormOutput_impl.hpp +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_IMPL_HPP - -#include -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -ErrorNormOutput::ErrorNormOutput( - const Teuchos::ParameterList& error_norm_list, - Teuchos::RCP> error_norm) - : _output_freq(std::numeric_limits::max()) - , _compute_time_error(true) - , _ostream(Teuchos::rcp(&std::cout, false)) - , _error_norm(error_norm) -{ - // Get output frequency - if (error_norm_list.isType("Output Frequency")) - { - _output_freq = error_norm_list.get("Output Frequency"); - } - - // Time integrated error norm - if (error_norm_list.isType("Compute Time Integral")) - { - _compute_time_error - = error_norm_list.get("Compute Time Integral"); - } - - // Initialize L1/L2 error norms objects - if (_compute_time_error) - { - _L1_time_error_norms = _error_norm->L1_errorNorms(); - _L2_time_error_norms = _error_norm->L2_errorNorms(); - } - - // Initialize output variable - _ostream.setShowProcRank(false); - _ostream.setOutputToRootOnly(0); -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeStartIntegrator( - const Tempus::Integrator& integrator) -{ - // Compute Initial Error Norms - const auto state = integrator.getSolutionHistory()->getCurrentState(); - _error_norm->ComputeNorms(state); - - // Print initial L1/L2 error norms - print_error_norms("Initial Spatial Integrated L1 Error Norms:", - _error_norm->L1_errorNorms()); - print_error_norms("Initial Spatial Integrated L2 Error Norms:", - _error_norm->L2_errorNorms()); -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeStartTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeNextTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeBeforeTakeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeAfterTakeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeAfterCheckTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - // Compute Error Norms - const auto state = integrator.getSolutionHistory()->getCurrentState(); - _error_norm->ComputeNorms(state); - const auto dt = state->getTimeStep(); - const auto current_index = state->getIndex(); - - // Compute integrated L1/L2 error norms - if (_compute_time_error) - { - for (std::size_t i = 0; i < _L1_time_error_norms.size(); ++i) - { - _L1_time_error_norms[i].error_norm - += _error_norm->L1_errorNorms()[i].error_norm * dt; - } - - for (std::size_t i = 0; i < _L2_time_error_norms.size(); ++i) - { - _L2_time_error_norms[i].error_norm - += _error_norm->L2_errorNorms()[i].error_norm * dt; - } - } - - // Print L1/L2 error norms - if (0 == current_index % _output_freq) - { - print_error_norms("Spatial Integrated L1 Error Norms:", - _error_norm->L1_errorNorms()); - print_error_norms("Spatial Integrated L2 Error Norms:", - _error_norm->L2_errorNorms()); - - if (_compute_time_error) - { - const double current_time = integrator.getTime(); - - print_error_norms("Temporal/Spatial Integrated L1 Error Norms:", - _L1_time_error_norms, - current_time); - - print_error_norms("Temporal/Spatial Integrated L2 Error Norms:", - _L2_time_error_norms, - current_time); - } - } -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::observeEndIntegrator( - const Tempus::Integrator& integrator) -{ - if (_compute_time_error) - { - // Get final time - const double final_time = integrator.getTime(); - - // L1 error norms - print_error_norms("Final Temporal/Spatial Integrated L1 Error Norms:", - _L1_time_error_norms, - final_time); - - // L2 error norms - print_error_norms("Final Temporal/Spatial Integrated L2 Error Norms:", - _L2_time_error_norms, - final_time); - } - else - { - // Print L1/L2 error norms - print_error_norms("Final Spatial Integrated L1 Error Norms:", - _error_norm->L1_errorNorms()); - print_error_norms("Final Spatial Integrated L2 Error Norms:", - _error_norm->L2_errorNorms()); - } -} - -//---------------------------------------------------------------------------// -template -void ErrorNormOutput::print_error_norms( - const std::string& heading, - const std::vector< - typename ComputeErrorNorms::ErrorNorms::DofErrorNorm>& error_norm, - const double scaling) -{ - _ostream << '\n' - << heading << '\n' - << std::setprecision(prec) << std::scientific; - for (auto& dof : error_norm) - { - _ostream << " " << dof.name << " = " << dof.error_norm / scaling - << '\n'; - } - _ostream << '\n'; -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSOBSERVER_ERRORNORMOUTPUT_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusObserver_IterationOutput.hpp b/src/observers/VertexCFD_TempusObserver_IterationOutput.hpp deleted file mode 100644 index ae9f513..0000000 --- a/src/observers/VertexCFD_TempusObserver_IterationOutput.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_HPP -#define VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_HPP - -#include "observers/VertexCFD_TempusTimeStepControl_Strategy.hpp" - -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class IterationOutput : virtual public Tempus::IntegratorObserver -{ - public: - IterationOutput( - Teuchos::RCP> dt_strategy); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - Teuchos::FancyOStream _ostream; - Teuchos::RCP> _dt_strategy; - int _time_precision = 3; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#include "VertexCFD_TempusObserver_IterationOutput_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_HPP diff --git a/src/observers/VertexCFD_TempusObserver_IterationOutput_impl.hpp b/src/observers/VertexCFD_TempusObserver_IterationOutput_impl.hpp deleted file mode 100644 index ee668b6..0000000 --- a/src/observers/VertexCFD_TempusObserver_IterationOutput_impl.hpp +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_IMPL_HPP - -#include -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -IterationOutput::IterationOutput( - Teuchos::RCP> dt_strategy) - : _ostream(Teuchos::rcp(&std::cout, false)) - , _dt_strategy(dt_strategy) -{ - _ostream.setShowProcRank(false); - _ostream.setOutputToRootOnly(0); -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeStartIntegrator( - const Tempus::Integrator& integrator) -{ - std::time_t begin = std::time(nullptr); - _ostream << "\n===========================================================" - "=================\n" - << "Time Integration Begin\n" - << std::asctime(std::localtime(&begin)) - << "\n Stepper = " << integrator.getStepper()->description() - << "\n Simulation Time Range [" - << integrator.getTimeStepControl()->getInitTime() << ", " - << integrator.getTimeStepControl()->getFinalTime() << "]" - << "\n-----------------------------------------------------------" - "-----------------\n"; - - auto tsc = integrator.getTimeStepControl(); - const auto final_time = tsc->getFinalTime(); - const auto dt_min = tsc->getMinTimeStep(); - - // Get location of first significant digit of a double - auto get_digit = [](double t) -> int { - return t > 0.0 ? std::floor(std::log10(t)) : 0; - }; - - const int t_digit = get_digit(final_time); - const int dt_digit = get_digit(dt_min); - - // Make sure we always get meaningful time precision - _time_precision = t_digit - dt_digit + 2; -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeStartTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeNextTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeBeforeTakeStep( - const Tempus::Integrator& integrator) -{ - const auto current_time = integrator.getTime(); - const auto state = integrator.getSolutionHistory()->getWorkingState(); - _ostream << "\nTime Step = " << state->getIndex(); - _ostream << "; Order = " << state->getOrder(); - _ostream << "\nCFL = " << std::setprecision(3) << std::scientific - << _dt_strategy->currentCFL(); - _ostream << "; dt = " << std::setprecision(3) << std::scientific - << state->getTimeStep(); - _ostream << "; Time = " << std::setprecision(_time_precision) - << std::scientific << current_time << "\n"; -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeAfterTakeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeAfterCheckTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - const auto stepper_time = integrator.getStepperTimer()->totalElapsedTime(); - integrator.getStepperTimer()->reset(); - _ostream << "Time step time to completion (s): " << std::setprecision(2) - << std::scientific << stepper_time; - _ostream << "\n"; -} - -//---------------------------------------------------------------------------// -template -void IterationOutput::observeEndIntegrator( - const Tempus::Integrator& integrator) -{ - std::string exit_status; - if (integrator.getSolutionHistory()->getCurrentState()->getSolutionStatus() - == Tempus::Status::FAILED - or integrator.getStatus() == Tempus::Status::FAILED) - { - exit_status = "Time integration FAILURE!"; - } - else - { - exit_status = "Time integration complete."; - } - std::time_t end = std::time(nullptr); - const auto runtime_sec - = integrator.getIntegratorTimer()->totalElapsedTime(); - const auto runtime_min = runtime_sec / 60; - const auto runtime_hr = runtime_min / 60; - _ostream << "\n-----------------------------------------------------------" - "-----------------\n" - << "Total runtime = " << std::setprecision(2) << std::scientific - << runtime_sec << " sec\n" - << " = " << std::setprecision(2) << std::scientific - << runtime_min << " min\n" - << " = " << std::setprecision(2) << std::fixed - << runtime_hr << " hr\n" - << std::asctime(std::localtime(&end)) << exit_status - << "\n===========================================================" - "=================\n" - << "\n"; -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSOBSERVER_ITERATIONOUTPUT_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusObserver_ResponseOutput.hpp b/src/observers/VertexCFD_TempusObserver_ResponseOutput.hpp deleted file mode 100644 index 5bff03d..0000000 --- a/src/observers/VertexCFD_TempusObserver_ResponseOutput.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_HPP -#define VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_HPP - -#include "responses/VertexCFD_ResponseManager.hpp" - -#include -#include - -#include -#include - -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class ResponseOutput : virtual public Tempus::IntegratorObserver -{ - public: - ResponseOutput(Teuchos::RCP response_manager, - std::vector output_freq); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - Teuchos::FancyOStream _ostream; - Teuchos::RCP _response_manager; - std::vector _output_freq; - - void outputResponses(const Tempus::Integrator& integrator, - const int current_index = 0); -}; - -//---------------------------------------------------------------------------// - -} // namespace TempusObserver -} // namespace VertexCFD - -#include "VertexCFD_TempusObserver_ResponseOutput_impl.hpp" - -#endif // VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_HPP diff --git a/src/observers/VertexCFD_TempusObserver_ResponseOutput_impl.hpp b/src/observers/VertexCFD_TempusObserver_ResponseOutput_impl.hpp deleted file mode 100644 index 81053a9..0000000 --- a/src/observers/VertexCFD_TempusObserver_ResponseOutput_impl.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_IMPL_HPP - -#include -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -ResponseOutput::ResponseOutput( - Teuchos::RCP response_manager, - std::vector output_freq) - : _ostream(Teuchos::rcp(&std::cout, false)) - , _response_manager(response_manager) - , _output_freq(std::move(output_freq)) -{ - _ostream.setShowProcRank(false); - _ostream.setOutputToRootOnly(0); -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeStartIntegrator( - const Tempus::Integrator& integrator) -{ - // When the initial time index is zero, this will output all responses. - // Otherwise, output will depend on specified frequencies. - outputResponses(integrator, integrator.getIndex()); -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeStartTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeNextTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeBeforeTakeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeAfterTakeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeAfterCheckTimeStep( - const Tempus::Integrator& /*integrator*/) -{ -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - // Output responses at specified frequencies based on time step index. - outputResponses(integrator, integrator.getIndex()); -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::observeEndIntegrator( - const Tempus::Integrator& integrator) -{ - // Output all responses unconditionally. - outputResponses(integrator); -} - -//---------------------------------------------------------------------------// -template -void ResponseOutput::outputResponses( - const Tempus::Integrator& integrator, const int current_index) -{ - const int num_resp = _response_manager->numResponses(); - - // Just return if there are no responses to output. - if (num_resp == 0) - return; - - _response_manager->deactivateAll(); - - // Activate desired responses. - int num_outputs = 0; - for (int i = 0; i < num_resp; ++i) - { - if (0 == current_index % _output_freq[i]) - { - _response_manager->activateResponse(i); - ++num_outputs; - } - } - - // Just return if there are no respones to output for this time step. - if (num_outputs == 0) - return; - - // Evaluate responses. - const auto state = integrator.getSolutionHistory()->getCurrentState(); - _response_manager->evaluateResponses(state->getX(), state->getXDot()); - - // Outupt the integrated values. - _ostream << "Scalar Responses:\n"; - for (int i = 0; i < num_resp; ++i) - { - if (0 == current_index % _output_freq[i]) - { - const auto& name = _response_manager->name(i); - const auto value = _response_manager->value(i); - - constexpr int prec = std::numeric_limits::digits10 + 1; - - _ostream << " " << name << " = " << std::setprecision(prec) - << value << '\n'; - } - } -} -//---------------------------------------------------------------------------// - -} // namespace TempusObserver -} // namespace VertexCFD - -#endif // VERTEXCFD_TEMPUSOBSERVER_RESPONSEOUTPUT_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteMatrix.hpp b/src/observers/VertexCFD_TempusObserver_WriteMatrix.hpp deleted file mode 100644 index 64fc3c8..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteMatrix.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_HPP - -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class WriteMatrix : virtual public Tempus::IntegratorObserver -{ - public: - WriteMatrix(const Teuchos::ParameterList& output_params); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - Teuchos::Array _write_steps; - std::string _jacobian_prefix; - std::string _residual_prefix; - bool _write_residual; - - // Extract Thyra linear operator from linear solver - Teuchos::RCP> extractLinearOp( - const Teuchos::RCP>& - linear_solver) const; - - // Extract forward op src (the Jacobian) from a linear op with solve - template - Teuchos::RCP> tryExtractForwardOp( - const Teuchos::RCP>& - linear_solver) const; - - // Write Jacobian matrix to file, toggling on Epetra vs. Tpetra - void - write_jacobian(const Teuchos::RCP>& matrix, - const std::string& filename, - const std::string& description) const; - - // Write residual to file, toggling on Epetra vs. Tpetra - void - write_residual(const Teuchos::RCP>& vec, - const std::string& filename, - const std::string& description) const; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#include "VertexCFD_TempusObserver_WriteMatrix_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteMatrix_impl.hpp b/src/observers/VertexCFD_TempusObserver_WriteMatrix_impl.hpp deleted file mode 100644 index 21abc15..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteMatrix_impl.hpp +++ /dev/null @@ -1,345 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -WriteMatrix::WriteMatrix(const Teuchos::ParameterList& write_params) -{ - // Determine which time steps will write out matrix files - if (write_params.isType>("Write Steps")) - { - _write_steps - = write_params.template get>("Write Steps"); - } - else - { - throw std::runtime_error( - "Requested matrix output but 'Write Steps' was not provided'"); - } - - // Determine if we want the residual in addition to matrix - _write_residual = false; - if (write_params.isType("Write Residual")) - { - _write_residual = write_params.template get("Write Residual"); - } - - // Set base filename for files. If not specified, use generic - // "jacobian" and "residual" - if (write_params.isType("Matrix File Prefix")) - { - auto base_prefix - = write_params.template get("Matrix File Prefix"); - _jacobian_prefix = base_prefix + "-jacobian"; - _residual_prefix = base_prefix + "-residual"; - } - else - { - _jacobian_prefix = "jacobian"; - _residual_prefix = "residual"; - } -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeStartIntegrator( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeStartTimeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeNextTimeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeBeforeTakeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeAfterTakeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeAfterCheckTimeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - auto step_index = integrator.getIndex(); - - // Only write solution at specified time steps - if (std::find(_write_steps.begin(), _write_steps.end(), step_index) - != _write_steps.end()) - { - // Determine filename for this step - std::string filename = _jacobian_prefix + "-" - + std::to_string(step_index) + ".mtx"; - - // Message to put into comments of file - std::string descr = "VertexCFD Jacobian matrix at time step " - + std::to_string(step_index); - - // Get nonlinear solver from time integrator - auto stepper = integrator.getStepper(); - auto nonlinear_solver = stepper->getSolver(); - if (!nonlinear_solver) - { - throw std::runtime_error("Nonlinear solver not available"); - } - - // Extract linear solver operator - auto linear_solver = nonlinear_solver->get_W(); - if (!linear_solver) - { - throw std::runtime_error("Linear solver is not available"); - } - - // Extra linear operator (Jacobian), toggling on Belos vs. AztecOO - auto jacobian_op = this->extractLinearOp(linear_solver); - - // Process matrix output, toggling on Epetra vs. Tpetra - this->write_jacobian(jacobian_op, filename, descr); - - if (_write_residual) - { - filename = _residual_prefix + "-" + std::to_string(step_index) - + ".mtx"; - descr = "Residual vector at time step " - + std::to_string(step_index); - - // Extract NOX solver - auto thyranox_solver - = Teuchos::rcp_dynamic_cast( - nonlinear_solver); - auto nox_solver = thyranox_solver->getNOXSolver(); - - // Get residual vector from the solver - // Use the _previous_ solution group so that the residual - // is the RHS from the last nonlinear iteration rather than - // the converged residual after the solve is complete. - const auto& group = nox_solver->getPreviousSolutionGroup(); - const auto& nox_resid = group.getF(); - const NOX::Thyra::Vector& noxthyra_resid - = dynamic_cast(nox_resid); - auto thyra_resid = noxthyra_resid.getThyraRCPVector(); - - // Process vector output, toggling on Epetra vs. Tpetra - this->write_residual(thyra_resid, filename, descr); - } - } -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::observeEndIntegrator(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -Teuchos::RCP> -WriteMatrix::extractLinearOp( - const Teuchos::RCP>& - linear_solver) const -{ - Teuchos::RCP> jacobian_op; - - // Currently support Belos, AztecOO, Amesos, Amesos2 solvers. - // These all implement the same "getOp" interface, but that method is - // NOT a member of the LinearOpWithSolve base class. - // Try casting to each of these in turn to extract the Jacobian - jacobian_op - = this->tryExtractForwardOp>( - linear_solver); - if (jacobian_op) - return jacobian_op; - - jacobian_op = this->tryExtractForwardOp( - linear_solver); - if (jacobian_op) - return jacobian_op; - - jacobian_op = this->tryExtractForwardOp( - linear_solver); - if (jacobian_op) - return jacobian_op; - - jacobian_op - = this->tryExtractForwardOp>( - linear_solver); - if (jacobian_op) - return jacobian_op; - - throw std::runtime_error( - "Unrecognized linear solver type in matrix output"); -} - -//---------------------------------------------------------------------------// -// Attempt to extract the Jacobian from a solver of the type specified by -// the SolverType template parameter. If the concrete solver is not of the -// specified type, a null RCP will be returned. -//---------------------------------------------------------------------------// -template -template -Teuchos::RCP> -WriteMatrix::tryExtractForwardOp( - const Teuchos::RCP>& - linear_solver) const -{ - Teuchos::RCP> jacobian_op; - - auto derived_solver - = Teuchos::rcp_dynamic_cast(linear_solver); - if (derived_solver) - { - // Const-cast and extract forward source operator (which is the - // Jacobian) - auto nonconst_derived_solver - = Teuchos::rcp_const_cast(derived_solver); - auto fwd_opsrc = nonconst_derived_solver->extract_fwdOpSrc(); - jacobian_op = fwd_opsrc->getOp(); - } - - return jacobian_op; -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::write_jacobian( - const Teuchos::RCP>& jacobian_op, - const std::string& filename, - const std::string& description) const -{ - // Toggle on Epetra vs. Tpetra - auto jacobian_epetra - = Teuchos::rcp_dynamic_cast(jacobian_op); - if (jacobian_epetra) - { - auto epetra_op = jacobian_epetra->epetra_op(); - auto epetra_mat - = Teuchos::rcp_dynamic_cast(epetra_op); - if (!epetra_mat) - { - throw std::runtime_error("Epetra operator is not a RowMatrix"); - } - - EpetraExt::RowMatrixToMatrixMarketFile( - filename.c_str(), *epetra_mat, description.c_str()); - } - else - { - // If not Epetra, must be Tpetra - auto jacobian_tpetra = Teuchos::rcp_dynamic_cast< - const Thyra::TpetraLinearOp>(jacobian_op); - if (!jacobian_tpetra) - { - throw std::runtime_error("Unexpected matrix type"); - } - - // Tpetra matrix output requires concrete type (CrsMatrix, not - // RowMatrix) - using TpetraMatrix = Tpetra:: - CrsMatrix; - auto tpetra_op = jacobian_tpetra->getConstTpetraOperator(); - auto tpetra_mat - = Teuchos::rcp_dynamic_cast(tpetra_op); - if (!tpetra_mat) - { - throw std::runtime_error("Tpetra operator is not a CrsMatrix"); - } - - Tpetra::MatrixMarket::Writer::writeSparseFile( - filename, *tpetra_mat, "", description); - } -} - -//---------------------------------------------------------------------------// -template -void WriteMatrix::write_residual( - const Teuchos::RCP>& vec, - const std::string& filename, - const std::string& description) const -{ - // Vector should be a DefaultSpmdVector (Epetra) or a TpetraVector. - auto spmd_vec - = Teuchos::rcp_dynamic_cast>( - vec); - if (spmd_vec) - { - auto space = vec->space(); - auto epetra_map = Thyra::get_Epetra_Map(space); - auto epetra_vec = Thyra::get_Epetra_Vector(vec, epetra_map); - - EpetraExt::MultiVectorToMatrixMarketFile( - filename.c_str(), *epetra_vec, "", description.c_str()); - } - - auto thyratpetra_vec = Teuchos::rcp_dynamic_cast< - const Thyra:: - TpetraVector>( - vec); - if (thyratpetra_vec) - { - auto tpetra_vec = thyratpetra_vec->getConstTpetraVector(); - - using TpetraMatrix = Tpetra:: - CrsMatrix; - Tpetra::MatrixMarket::Writer::writeDenseFile( - filename, *tpetra_vec, "", description); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITEMATRIX_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteRestart.hpp b/src/observers/VertexCFD_TempusObserver_WriteRestart.hpp deleted file mode 100644 index 2498160..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteRestart.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_HPP - -#include "mesh/VertexCFD_Mesh_Restart.hpp" - -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class WriteRestart : virtual public Tempus::IntegratorObserver -{ - public: - WriteRestart(const Teuchos::RCP& restart_writer, - const Teuchos::ParameterList& output_params); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - void writeSolution(const Tempus::Integrator& integrator); - - private: - Teuchos::RCP _restart_writer; - int _write_frequency = 1; - int _last_index = -1; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#include "VertexCFD_TempusObserver_WriteRestart_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteRestart_impl.hpp b/src/observers/VertexCFD_TempusObserver_WriteRestart_impl.hpp deleted file mode 100644 index 90d3518..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteRestart_impl.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_IMPL_HPP - -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -WriteRestart::WriteRestart( - const Teuchos::RCP& restart_writer, - const Teuchos::ParameterList& output_params) - : _restart_writer(restart_writer) -{ - if (output_params.isType("Restart Write Frequency")) - { - _write_frequency - = output_params.template get("Restart Write Frequency"); - } -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeStartIntegrator( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeStartTimeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeNextTimeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeBeforeTakeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeAfterTakeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeAfterCheckTimeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - // Only write solution at specified time step intervals. - if (0 == integrator.getIndex() % _write_frequency) - { - writeSolution(integrator); - } -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::observeEndIntegrator( - const Tempus::Integrator& integrator) -{ - // Write out final solution, but only if this time step has not been - // written already. - if (integrator.getIndex() > _last_index) - { - writeSolution(integrator); - } -} - -//---------------------------------------------------------------------------// -template -void WriteRestart::writeSolution( - const Tempus::Integrator& integrator) -{ - auto time = integrator.getTime(); - auto solution = integrator.getSolutionHistory()->findState(time)->getX(); - auto solution_dot - = integrator.getSolutionHistory()->findState(time)->getXDot(); - _last_index = integrator.getIndex(); - _restart_writer->writeSolution(solution, solution_dot, _last_index, time); -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITERESTART_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteToExodus.hpp b/src/observers/VertexCFD_TempusObserver_WriteToExodus.hpp deleted file mode 100644 index 8524f42..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteToExodus.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_HPP - -#include "mesh/VertexCFD_Mesh_ExodusWriter.hpp" - -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -class WriteToExodus : virtual public Tempus::IntegratorObserver -{ - public: - WriteToExodus(const Teuchos::RCP& exodus_writer, - const Teuchos::ParameterList& output_params); - - /// Observe the beginning of the time integrator. - void observeStartIntegrator( - const Tempus::Integrator& integrator) override; - - /// Observe the beginning of the time step loop. - void - observeStartTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe after the next time step size is selected. The - /// observer can choose to change the current integratorStatus. - void - observeNextTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe before Stepper takes step. - void - observeBeforeTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after Stepper takes step. - void - observeAfterTakeStep(const Tempus::Integrator& integrator) override; - - /// Observe after checking time step. Observer can still fail the time step - /// here. - void observeAfterCheckTimeStep( - const Tempus::Integrator& integrator) override; - - /// Observe the end of the time step loop. - void - observeEndTimeStep(const Tempus::Integrator& integrator) override; - - /// Observe the end of the time integrator. - void - observeEndIntegrator(const Tempus::Integrator& integrator) override; - - private: - void writeSolution(const Tempus::Integrator& integrator); - - private: - Teuchos::RCP _exodus_writer; - int _write_frequency; - int _last_index = -1; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#include "VertexCFD_TempusObserver_WriteToExodus_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_HPP diff --git a/src/observers/VertexCFD_TempusObserver_WriteToExodus_impl.hpp b/src/observers/VertexCFD_TempusObserver_WriteToExodus_impl.hpp deleted file mode 100644 index e1331a0..0000000 --- a/src/observers/VertexCFD_TempusObserver_WriteToExodus_impl.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_IMPL_HPP -#define VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_IMPL_HPP - -#include -#include - -namespace VertexCFD -{ -namespace TempusObserver -{ -//---------------------------------------------------------------------------// -template -WriteToExodus::WriteToExodus( - const Teuchos::RCP& exodus_writer, - const Teuchos::ParameterList& output_params) - : _exodus_writer(exodus_writer) - , _write_frequency(1) -{ - _write_frequency - = output_params.template get("Exodus Write Frequency"); -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeStartIntegrator( - const Tempus::Integrator& integrator) -{ - // Write out initial conditions. - writeSolution(integrator); -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeStartTimeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeNextTimeStep(const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeBeforeTakeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeAfterTakeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeAfterCheckTimeStep( - const Tempus::Integrator&) -{ -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeEndTimeStep( - const Tempus::Integrator& integrator) -{ - // Only write solution at specified time step intervals. - if (0 == integrator.getIndex() % _write_frequency) - { - writeSolution(integrator); - } -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::observeEndIntegrator( - const Tempus::Integrator& integrator) -{ - // Write out final solution, but only if this time step has not been - // written already. - if (integrator.getIndex() > _last_index) - { - writeSolution(integrator); - } -} - -//---------------------------------------------------------------------------// -template -void WriteToExodus::writeSolution( - const Tempus::Integrator& integrator) -{ - const auto state = integrator.getSolutionHistory()->getCurrentState(); - const auto time = state->getTime(); - const auto time_step = state->getTimeStep(); - const auto solution = state->getX(); - const auto solution_dot = state->getXDot(); - _exodus_writer->writeSolution(solution, solution_dot, time, time_step); - _last_index = integrator.getIndex(); -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusObserver -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSOBSERVER_WRITETOEXODUS_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp b/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp deleted file mode 100644 index 39d379f..0000000 --- a/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_HPP -#define VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_HPP - -#include "observers/VertexCFD_TempusTimeStepControl_Strategy.hpp" - -#include "drivers/VertexCFD_PhysicsManager.hpp" -#include "responses/VertexCFD_ResponseManager.hpp" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusTimeStepControl -{ -//---------------------------------------------------------------------------// -template -class GlobalCFL : virtual public Strategy -{ - public: - GlobalCFL(const Teuchos::ParameterList& user_params, - Teuchos::RCP physics_manager); - - // Determine the time step size. - void setNextTimeStep( - const Tempus::TimeStepControl& tsc, - Teuchos::RCP> solution_history, - Tempus::Status& integrator_status) override; - - private: - enum class CflTransitionType - { - steps, - time - }; - - double _cfl; - double _cfl_init; - double _cfl_transition_init; - double _cfl_transition; - CflTransitionType _cfl_type; - Response::ResponseManager _response_manager; -}; - -//---------------------------------------------------------------------------// - -} // end namespace TempusTimeStepControl -} // end namespace VertexCFD - -#include "VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp" - -#endif // end VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_HPP diff --git a/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp b/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp deleted file mode 100644 index c8f883f..0000000 --- a/src/observers/VertexCFD_TempusTimeStepControl_GlobalCFL_impl.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_IMPL_HPP -#define VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_IMPL_HPP - -#include "VertexCFD_TempusTimeStepControl_GlobalCFL.hpp" - -#include -#include - -namespace VertexCFD -{ -namespace TempusTimeStepControl -{ -//---------------------------------------------------------------------------// -template -GlobalCFL::GlobalCFL(const Teuchos::ParameterList& user_params, - Teuchos::RCP physics_manager) - : _cfl(user_params.get("CFL")) - , _cfl_init(user_params.isType("CFL_init") - ? user_params.get("CFL_init") - : _cfl) - , _cfl_transition_init(0.0) - , _cfl_transition(0.0) - , _cfl_type(CflTransitionType::steps) - , _response_manager(physics_manager) -{ - // Validate transition type if present - if (user_params.isType("CFL_transition_type")) - { - const auto type_validator = Teuchos::rcp( - new Teuchos::StringToIntegralParameterEntryValidator( - Teuchos::tuple("steps", "time"), "steps")); - _cfl_type = type_validator->getIntegralValue( - user_params.get("CFL_transition_type")); - } - - // Check for ramping options - if (user_params.isType("CFL_transition")) - { - _cfl_transition = user_params.get("CFL_transition"); - if (user_params.isType("CFL_transition_init")) - { - _cfl_transition_init - = user_params.get("CFL_transition_init"); - } - } - else if (user_params.isType("CFL_transition_init")) - { - const std::string msg - = "\n\n'CFL_transition_init' must be" - "specified with 'CFL_transition'\n" - "in the input file. Please add 'CFL_transition'\n" - "to your input file.\n"; - throw std::runtime_error(msg); - } - - this->setCurrentCFL(_cfl_init); - - _response_manager.addMinValueResponse("global_cfl_time_step", "local_dt"); - - // For the new Tempus::TimeStepControlStrategy interface, we need to - // set a few base class member variables. In particular, incorrect - // behavior may result if "stepType_" is not set to "Variable". - this->stepType_ = "Variable"; - this->strategyType_ = "CFL Strategy"; - this->name_ = "CFL Strategy"; -} - -//---------------------------------------------------------------------------// -// Determine the time step size. -template -void GlobalCFL::setNextTimeStep( - const Tempus::TimeStepControl& tsc, - Teuchos::RCP> solution_history, - Tempus::Status&) -{ - // Get the working state. - auto working_state = solution_history->getWorkingState(); - - // Get minimum time step that ensures CFL <= 1 - _response_manager.evaluateResponses(working_state->getX(), - working_state->getXDot()); - const double dt_cfl1 = _response_manager.value(); - - // Compute linear weight based on input parameters - double wt; - if (_cfl_transition > 0) - { - // Current time step index/time - double dt_index = 0.0; - if (_cfl_type == CflTransitionType::steps) - dt_index = static_cast(solution_history->getCurrentIndex()); - else - dt_index = solution_history->getCurrentTime(); - - if (dt_index < _cfl_transition_init) - { - wt = 0.0; - } - else if (dt_index >= _cfl_transition_init - && dt_index <= _cfl_transition + _cfl_transition_init) - { - wt = std::min((dt_index - _cfl_transition_init) / _cfl_transition, - 1.0); - } - else - { - wt = 1.0; - } - } - else - { - wt = 1.0; - } - - // Compute time step based on desired CFL - const double cfl_desired = (1.0 - wt) * _cfl_init + wt * _cfl; - double dt = cfl_desired * dt_cfl1; - - // If it is out of the bounds specified by the user then restrict it - // before setting it. - const int dt_index = working_state->getIndex() - 1; - if (dt_index == 0 && tsc.getInitTimeStep() != tsc.getMinTimeStep()) - { - dt = tsc.getInitTimeStep(); - } - else if (dt < tsc.getMinTimeStep()) - { - dt = tsc.getMinTimeStep(); - } - else if (dt > tsc.getMaxTimeStep()) - { - dt = tsc.getMaxTimeStep(); - } - working_state->setTimeStep(dt); - working_state->setTime(solution_history->getCurrentTime() + dt); - - // Save current CFL so it may be accessed elsewhere - this->setCurrentCFL(dt / dt_cfl1); -} - -//---------------------------------------------------------------------------// - -} // end namespace TempusTimeStepControl -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALCFL_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp b/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp deleted file mode 100644 index 04164b1..0000000 --- a/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_HPP -#define VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_HPP - -#include "observers/VertexCFD_TempusTimeStepControl_Strategy.hpp" - -#include "drivers/VertexCFD_PhysicsManager.hpp" -#include "responses/VertexCFD_ResponseManager.hpp" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusTimeStepControl -{ -//---------------------------------------------------------------------------// -template -class GlobalTimeStep : virtual public Strategy -{ - public: - GlobalTimeStep(const Teuchos::ParameterList& user_params, - Teuchos::RCP physics_manager); - - // Determine the time step size. - void setNextTimeStep( - const Tempus::TimeStepControl& tsc, - Teuchos::RCP> solution_history, - Tempus::Status& integrator_status) override; - - private: - int _dt_transition_steps; - Response::ResponseManager _response_manager; -}; - -//---------------------------------------------------------------------------// - -} // namespace TempusTimeStepControl -} // namespace VertexCFD - -#include "VertexCFD_TempusTimeStepControl_GlobalTimeStep_impl.hpp" - -#endif // VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_HPP diff --git a/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep_impl.hpp b/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep_impl.hpp deleted file mode 100644 index 0751a79..0000000 --- a/src/observers/VertexCFD_TempusTimeStepControl_GlobalTimeStep_impl.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_IMPL_HPP -#define VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_IMPL_HPP - -#include "VertexCFD_TempusTimeStepControl_GlobalTimeStep.hpp" -#include - -namespace VertexCFD -{ -namespace TempusTimeStepControl -{ -//---------------------------------------------------------------------------// -template -GlobalTimeStep::GlobalTimeStep( - const Teuchos::ParameterList& user_params, - Teuchos::RCP physics_manager) - : _dt_transition_steps(user_params.isType("Time step transition " - "steps") - ? user_params.get("Time step transition " - "steps") - : 0) - , _response_manager(physics_manager) -{ - _response_manager.addMinValueResponse("global_cfl_time_step", "local_dt"); - - // For the new Tempus::TimeStepControlStrategy interface, we need to - // set a few base class member variables. In particular, incorrect - // behavior may result if "stepType_" is not set to "Variable". - this->stepType_ = "Variable"; - this->strategyType_ = "Global Time Step Strategy"; - this->name_ = "Global Time Step Strategy"; -} - -//---------------------------------------------------------------------------// -// Determine the time step size. -template -void GlobalTimeStep::setNextTimeStep( - const Tempus::TimeStepControl& tsc, - Teuchos::RCP> solution_history, - Tempus::Status&) -{ - // Get the working state. - auto working_state = solution_history->getWorkingState(); - - // Get minimum time step that ensures CFL <= 1 - _response_manager.evaluateResponses(working_state->getX(), - working_state->getXDot()); - const double dt_cfl1 = _response_manager.value(); - - // Get time step index (1-based) and compute linear weight - const auto dt_index = working_state->getIndex() - 1; - const auto wt - = _dt_transition_steps > 0 - ? std::min(static_cast(dt_index) / _dt_transition_steps, - 1.0) - : 1.0; - - const double dt_init - = std::max(tsc.getMinTimeStep(), tsc.getInitTimeStep()); - const double dt_final = tsc.getMaxTimeStep(); - const double dt = (1.0 - wt) * dt_init + wt * dt_final; - - working_state->setTimeStep(dt); - working_state->setTime(solution_history->getCurrentState()->getTime() + dt); - - // Save current CFL so it may be accessed elsewhere - this->setCurrentCFL(dt / dt_cfl1); -} - -//---------------------------------------------------------------------------// - -} // namespace TempusTimeStepControl -} // namespace VertexCFD - -#endif // VERTEXCFD_TEMPUSTIMESTEPCONTROL_GLOBALTIMESTEP_IMPL_HPP diff --git a/src/observers/VertexCFD_TempusTimeStepControl_Strategy.hpp b/src/observers/VertexCFD_TempusTimeStepControl_Strategy.hpp deleted file mode 100644 index 284fd90..0000000 --- a/src/observers/VertexCFD_TempusTimeStepControl_Strategy.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef VERTEXCFD_TEMPUSTIMESTEPCONTROL_STRATEGY_HPP -#define VERTEXCFD_TEMPUSTIMESTEPCONTROL_STRATEGY_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace TempusTimeStepControl -{ -//---------------------------------------------------------------------------// -template -class Strategy : virtual public Tempus::TimeStepControlStrategy -{ - public: - virtual ~Strategy() = default; - double currentCFL() const { return _cfl_current; } - - protected: - void setCurrentCFL(const double cfl) { _cfl_current = cfl; } - - private: - double _cfl_current = 0.0; -}; - -//---------------------------------------------------------------------------// - -} // namespace TempusTimeStepControl -} // namespace VertexCFD - -#endif // VERTEXCFD_TEMPUSTIMESTEPCONTROL_STRATEGY_HPP diff --git a/src/observers/unit_test/CDR_Model.hpp b/src/observers/unit_test/CDR_Model.hpp deleted file mode 100644 index 62bd5be..0000000 --- a/src/observers/unit_test/CDR_Model.hpp +++ /dev/null @@ -1,175 +0,0 @@ -// @HEADER -// **************************************************************************** -// Tempus: Copyright (2017) Sandia Corporation -// -// Distributed under BSD 3-clause license (See accompanying file Copyright.txt) -// **************************************************************************** -// @HEADER - -// **************************************************************************** -// Adapted for use in VertexCFD from Trilinos/packages/tempus/test/TestModels -// **************************************************************************** - -#ifndef VERTEXCFD_CDR_MODEL_HPP -#define VERTEXCFD_CDR_MODEL_HPP - -#include "Thyra_StateFuncModelEvaluatorBase.hpp" - -class Epetra_Comm; -class Epetra_Map; -class Epetra_Vector; -class Epetra_CrsGraph; -class Epetra_Import; - -namespace VertexCFD -{ -namespace Test -{ -template -class ModelEvaluator1DFEM; - -/** \brief 1D CGFEM model for convection/diffusion/reaction - * - * The equation modeled is: - - \verbatim - -dT dT d^2(T) --- + a -- + ------ - K * T**2 = 0 -dt dz dz^2 - - subject to: - T = 1.0 @ z = z_min - - \endverbatim - - * The Matrix W = d(f)/d(x) is implemented as a - * Thyra::MultiVectorBase object and the class - * Thyra::DefaultSerialDenseLinearOpWithSolveFactory is used to - * create the linear solver. - */ -template -class CDR_Model final : public ::Thyra::StateFuncModelEvaluatorBase -{ - public: - CDR_Model(const Teuchos::RCP& comm, - const int num_global_elements, - const Scalar z_min, - const Scalar z_max, - const Scalar a, // convection - const Scalar k); // source - - /** \name Initializers/Accessors */ - //@{ - - void set_x0(const Teuchos::ArrayView& x0); - - void setShowGetInvalidArgs(bool showGetInvalidArg); - - void set_W_factory( - const Teuchos::RCP>& - W_factory); - - //@} - - /** \name Public functions overridden from ModelEvaluator. */ - //@{ - - Teuchos::RCP> - get_x_space() const override; - Teuchos::RCP> - get_f_space() const override; - ::Thyra::ModelEvaluatorBase::InArgs - getNominalValues() const override; - Teuchos::RCP> - create_W() const override; - Teuchos::RCP<::Thyra::LinearOpBase> create_W_op() const override; - Teuchos::RCP> - get_W_factory() const override; - ::Thyra::ModelEvaluatorBase::InArgs createInArgs() const override; - Teuchos::RCP<::Thyra::PreconditionerBase> - create_W_prec() const override; - //@} - - private: - /** Allocates and returns the Jacobian matrix graph */ - Teuchos::RCP createGraph(); - - /** \name Private functions overridden from ModelEvaluatorDefaultBase. */ - //@{ - - ::Thyra::ModelEvaluatorBase::OutArgs - createOutArgsImpl() const override; - void - evalModelImpl(const ::Thyra::ModelEvaluatorBase::InArgs& inArgs, - const ::Thyra::ModelEvaluatorBase::OutArgs& outArgs) - const override; - - //@} - - private: // data members - const Teuchos::RCP comm_; - const int num_global_elements_; - const Scalar z_min_; - const Scalar z_max_; - const Scalar a_; - const Scalar k_; - - Teuchos::RCP> x_space_; - Teuchos::RCP x_owned_map_; - Teuchos::RCP x_ghosted_map_; - Teuchos::RCP importer_; - - Teuchos::RCP> f_space_; - Teuchos::RCP f_owned_map_; - - Teuchos::RCP W_graph_; - - Teuchos::RCP> W_factory_; - - Teuchos::RCP node_coordinates_; - Teuchos::RCP ghosted_node_coordinates_; - - mutable Teuchos::RCP u_ptr; - mutable Teuchos::RCP u_dot_ptr; - mutable Teuchos::RCP x_ptr; - - mutable Teuchos::RCP J_diagonal_; - - ::Thyra::ModelEvaluatorBase::InArgs nominalValues_; - Teuchos::RCP<::Thyra::VectorBase> x0_; - Teuchos::Array p_; - bool showGetInvalidArg_; - ::Thyra::ModelEvaluatorBase::InArgs prototypeInArgs_; - ::Thyra::ModelEvaluatorBase::OutArgs prototypeOutArgs_; -}; - -//================================================================== -// Finite Element Basis Object -class Basis -{ - public: - // Constructor - Basis(); - - // Destructor - ~Basis(); - - // Calculates the values of u and x at the specified gauss point - void computeBasis(int gp, double* z, double* u, double* u_dot = nullptr); - - public: - // Variables that are calculated at the gauss point - double *phi, *dphide; - double uu, zz, duu, eta, wt; - double dz; - // These are only needed for transient - double uu_dot, duu_dot; -}; - -} // namespace Test -} // namespace VertexCFD - -#include "CDR_Model_impl.hpp" - -#endif diff --git a/src/observers/unit_test/CDR_Model_impl.hpp b/src/observers/unit_test/CDR_Model_impl.hpp deleted file mode 100644 index 7700070..0000000 --- a/src/observers/unit_test/CDR_Model_impl.hpp +++ /dev/null @@ -1,637 +0,0 @@ -// @HEADER -// **************************************************************************** -// Tempus: Copyright (2017) Sandia Corporation -// -// Distributed under BSD 3-clause license (See accompanying file Copyright.txt) -// **************************************************************************** -// @HEADER - -#ifndef VERTEXCFD_CDR_MODEL_IMPL_HPP -#define VERTEXCFD_CDR_MODEL_IMPL_HPP - -// Thyra support -#include "Thyra_DefaultSerialDenseLinearOpWithSolveFactory.hpp" -#include "Thyra_DefaultSpmdVectorSpace.hpp" -#include "Thyra_DetachedMultiVectorView.hpp" -#include "Thyra_DetachedVectorView.hpp" -#include "Thyra_MultiVectorStdOps.hpp" -#include "Thyra_PreconditionerBase.hpp" -#include "Thyra_VectorStdOps.hpp" - -// Epetra support -#include "Epetra_Comm.h" -#include "Epetra_CrsGraph.h" -#include "Epetra_CrsMatrix.h" -#include "Epetra_Import.h" -#include "Epetra_Map.h" -#include "Epetra_Vector.h" -#include "Thyra_EpetraLinearOp.hpp" -#include "Thyra_EpetraThyraWrappers.hpp" -#include "Thyra_get_Epetra_Operator.hpp" - -namespace VertexCFD -{ -namespace Test -{ -// Constructor - -template -CDR_Model::CDR_Model(const Teuchos::RCP& comm, - const int num_global_elements, - const Scalar z_min, - const Scalar z_max, - const Scalar a, - const Scalar k) - : comm_(comm) - , num_global_elements_(num_global_elements) - , z_min_(z_min) - , z_max_(z_max) - , a_(a) - , k_(k) - , showGetInvalidArg_(false) -{ - using Teuchos::RCP; - using Teuchos::rcp; - using ::Thyra::VectorBase; - typedef ::Thyra::ModelEvaluatorBase MEB; - typedef Teuchos::ScalarTraits ST; - - TEUCHOS_ASSERT(nonnull(comm_)); - - const int num_nodes = num_global_elements_ + 1; - - // owned space - x_owned_map_ = rcp(new Epetra_Map(num_nodes, 0, *comm_)); - x_space_ = ::Thyra::create_VectorSpace(x_owned_map_); - - // ghosted space - if (comm_->NumProc() == 1) - { - x_ghosted_map_ = x_owned_map_; - } - else - { - int OverlapNumMyElements; - int OverlapMinMyGID; - OverlapNumMyElements = x_owned_map_->NumMyElements() + 2; - if ((comm_->MyPID() == 0) || (comm_->MyPID() == (comm_->NumProc() - 1))) - OverlapNumMyElements--; - - if (comm_->MyPID() == 0) - OverlapMinMyGID = x_owned_map_->MinMyGID(); - else - OverlapMinMyGID = x_owned_map_->MinMyGID() - 1; - - int* OverlapMyGlobalElements = new int[OverlapNumMyElements]; - - for (int i = 0; i < OverlapNumMyElements; i++) - OverlapMyGlobalElements[i] = OverlapMinMyGID + i; - - x_ghosted_map_ = Teuchos::rcp(new Epetra_Map( - -1, OverlapNumMyElements, OverlapMyGlobalElements, 0, *comm_)); - - delete[] OverlapMyGlobalElements; - } - - importer_ = Teuchos::rcp(new Epetra_Import(*x_ghosted_map_, *x_owned_map_)); - - // residual space - f_owned_map_ = x_owned_map_; - f_space_ = x_space_; - - x0_ = ::Thyra::createMember(x_space_); - V_S(x0_.ptr(), ST::zero()); - - // set_p(Teuchos::tuple(p0, p1)()); - // set_x0(Teuchos::tuple(x0, x1)()); - - // Initialize the graph for W CrsMatrix object - W_graph_ = createGraph(); - - // Create the nodal coordinates - { - node_coordinates_ = Teuchos::rcp(new Epetra_Vector(*x_owned_map_)); - Scalar length = z_max_ - z_min_; - Scalar dx = length / ((double)num_global_elements_ - 1); - for (int i = 0; i < x_owned_map_->NumMyElements(); i++) - { - (*node_coordinates_)[i] - = z_min_ + dx * ((double)x_owned_map_->MinMyGID() + i); - } - } - - MEB::InArgsSetup inArgs; - inArgs.setModelEvalDescription(this->description()); - inArgs.setSupports(MEB::IN_ARG_t); - inArgs.setSupports(MEB::IN_ARG_x); - inArgs.setSupports(MEB::IN_ARG_beta); - inArgs.setSupports(MEB::IN_ARG_x_dot); - inArgs.setSupports(MEB::IN_ARG_alpha); - prototypeInArgs_ = inArgs; - - MEB::OutArgsSetup outArgs; - outArgs.setModelEvalDescription(this->description()); - outArgs.setSupports(MEB::OUT_ARG_f); - outArgs.setSupports(MEB::OUT_ARG_W); - outArgs.setSupports(MEB::OUT_ARG_W_op); - outArgs.setSupports(MEB::OUT_ARG_W_prec); - // outArgs.set_W_properties(DerivativeProperties( - // DERIV_LINEARITY_NONCONST - // ,DERIV_RANK_FULL - // ,true // supportsAdjoint - // )); - prototypeOutArgs_ = outArgs; - - // Setup nominal values - nominalValues_ = inArgs; - nominalValues_.set_x(x0_); - auto x_dot_init = Thyra::createMember(this->get_x_space()); - Thyra::put_scalar(Scalar(0.0), x_dot_init.ptr()); - nominalValues_.set_x_dot(x_dot_init); -} - -// Initializers/Accessors - -template -Teuchos::RCP CDR_Model::createGraph() -{ - Teuchos::RCP W_graph; - - // Create the shell for the - W_graph = Teuchos::rcp(new Epetra_CrsGraph(Copy, *x_owned_map_, 5)); - - // Declare required variables - int row; - int column; - int OverlapNumMyElements = x_ghosted_map_->NumMyElements(); - - // Loop Over # of Finite Elements on Processor - for (int ne = 0; ne < OverlapNumMyElements - 1; ne++) - { - // Loop over Nodes in Element - for (int i = 0; i < 2; i++) - { - row = x_ghosted_map_->GID(ne + i); - - // Loop over Trial Functions - for (int j = 0; j < 2; j++) - { - // If this row is owned by current processor, add the index - if (x_owned_map_->MyGID(row)) - { - column = x_ghosted_map_->GID(ne + j); - W_graph->InsertGlobalIndices(row, 1, &column); - } - } - } - } - W_graph->FillComplete(); - return W_graph; -} - -template -void CDR_Model::set_x0(const Teuchos::ArrayView& x0_in) -{ -#ifdef TEUCHOS_DEBUG - TEUCHOS_ASSERT_EQUALITY(x_space_->dim(), x0_in.size()); -#endif - Thyra::DetachedVectorView x0(x0_); - x0.sv().values()().assign(x0_in); -} - -template -void CDR_Model::setShowGetInvalidArgs(bool showGetInvalidArg) -{ - showGetInvalidArg_ = showGetInvalidArg; -} - -template -void CDR_Model::set_W_factory( - const Teuchos::RCP>& - W_factory) -{ - W_factory_ = W_factory; -} - -// Public functions overridden from ModelEvaluator - -template -Teuchos::RCP> -CDR_Model::get_x_space() const -{ - return x_space_; -} - -template -Teuchos::RCP> -CDR_Model::get_f_space() const -{ - return f_space_; -} - -template -Thyra::ModelEvaluatorBase::InArgs -CDR_Model::getNominalValues() const -{ - return nominalValues_; -} - -template -Teuchos::RCP> -CDR_Model::create_W() const -{ - Teuchos::RCP> W_factory - = this->get_W_factory(); - - TEUCHOS_TEST_FOR_EXCEPTION(is_null(W_factory), - std::runtime_error, - "W_factory in CDR_Model has a null W_factory!"); - - Teuchos::RCP> matrix = this->create_W_op(); - - Teuchos::RCP> W - = Thyra::linearOpWithSolve(*W_factory, matrix); - - return W; -} - -template -Teuchos::RCP> CDR_Model::create_W_op() const -{ - Teuchos::RCP W_epetra - = Teuchos::rcp(new Epetra_CrsMatrix(::Copy, *W_graph_)); - - return Thyra::nonconstEpetraLinearOp(W_epetra); -} - -template -Teuchos::RCP<::Thyra::PreconditionerBase> -CDR_Model::create_W_prec() const -{ - Teuchos::RCP W_epetra - = Teuchos::rcp(new Epetra_CrsMatrix(::Copy, *W_graph_)); - - const Teuchos::RCP> W_op - = Thyra::nonconstEpetraLinearOp(W_epetra); - - Teuchos::RCP> prec - = Teuchos::rcp(new Thyra::DefaultPreconditioner); - - prec->initializeRight(W_op); - - return prec; -} - -template -Teuchos::RCP> -CDR_Model::get_W_factory() const -{ - return W_factory_; -} - -template -Thyra::ModelEvaluatorBase::InArgs -CDR_Model::createInArgs() const -{ - return prototypeInArgs_; -} - -// Private functions overridden from ModelEvaluatorDefaultBase - -template -Thyra::ModelEvaluatorBase::OutArgs -CDR_Model::createOutArgsImpl() const -{ - return prototypeOutArgs_; -} - -template -void CDR_Model::evalModelImpl( - const Thyra::ModelEvaluatorBase::InArgs& inArgs, - const Thyra::ModelEvaluatorBase::OutArgs& outArgs) const -{ - using Teuchos::RCP; - using Teuchos::rcp; - using Teuchos::rcp_dynamic_cast; - - TEUCHOS_ASSERT(nonnull(inArgs.get_x())); - TEUCHOS_ASSERT(nonnull(inArgs.get_x_dot())); - - // const Thyra::ConstDetachedVectorView x(inArgs.get_x()); - - const RCP> f_out = outArgs.get_f(); - const RCP> W_out = outArgs.get_W_op(); - const RCP> W_prec_out - = outArgs.get_W_prec(); - - if (nonnull(f_out) || nonnull(W_out) || nonnull(W_prec_out)) - { - // **************** - // Get the underlying epetra objects - // **************** - - RCP f; - if (nonnull(f_out)) - { - f = Thyra::get_Epetra_Vector(*f_owned_map_, outArgs.get_f()); - } - - RCP J; - if (nonnull(W_out)) - { - RCP W_epetra = Thyra::get_Epetra_Operator(*W_out); - J = rcp_dynamic_cast(W_epetra); - TEUCHOS_ASSERT(nonnull(J)); - } - - RCP M_inv; - if (nonnull(W_prec_out)) - { - RCP M_epetra = Thyra::get_Epetra_Operator( - *(W_prec_out->getNonconstRightPrecOp())); - M_inv = rcp_dynamic_cast(M_epetra); - TEUCHOS_ASSERT(nonnull(M_inv)); - J_diagonal_ = Teuchos::rcp(new Epetra_Vector(*x_owned_map_)); - } - - // **************** - // Create ghosted objects - // **************** - - // Set the boundary condition directly. Works for both x and xDot - // solves. - if (comm_->MyPID() == 0) - { - RCP> x - = Teuchos::rcp_const_cast>( - inArgs.get_x()); - (*Thyra::get_Epetra_Vector(*x_owned_map_, x))[0] = 1.0; - } - - if (is_null(u_ptr)) - u_ptr = Teuchos::rcp(new Epetra_Vector(*x_ghosted_map_)); - - u_ptr->Import( - *(Thyra::get_Epetra_Vector(*x_owned_map_, inArgs.get_x())), - *importer_, - Insert); - - if (is_null(u_dot_ptr)) - u_dot_ptr = Teuchos::rcp(new Epetra_Vector(*x_ghosted_map_)); - - u_dot_ptr->Import( - *(Thyra::get_Epetra_Vector(*x_owned_map_, inArgs.get_x_dot())), - *importer_, - Insert); - - if (is_null(x_ptr)) - { - x_ptr = Teuchos::rcp(new Epetra_Vector(*x_ghosted_map_)); - x_ptr->Import(*node_coordinates_, *importer_, Insert); - } - - Epetra_Vector& u = *u_ptr; - Epetra_Vector& u_dot = *u_dot_ptr; - Epetra_Vector& x = *x_ptr; - - int ierr = 0; - int OverlapNumMyElements = x_ghosted_map_->NumMyElements(); - - double xx[2]; - double uu[2]; - double uu_dot[2]; - Basis basis; - const auto alpha = inArgs.get_alpha(); - const auto beta = inArgs.get_beta(); - - // Zero out the objects that will be filled - if (nonnull(f)) - f->PutScalar(0.0); - if (nonnull(J)) - J->PutScalar(0.0); - if (nonnull(M_inv)) - M_inv->PutScalar(0.0); - - // Loop Over # of Finite Elements on Processor - for (int ne = 0; ne < OverlapNumMyElements - 1; ne++) - { - // Loop Over Gauss Points - for (int gp = 0; gp < 2; gp++) - { - // Get the solution and coordinates at the nodes - xx[0] = x[ne]; - xx[1] = x[ne + 1]; - uu[0] = u[ne]; - uu[1] = u[ne + 1]; - uu_dot[0] = u_dot[ne]; - uu_dot[1] = u_dot[ne + 1]; - // Calculate the basis function at the gauss point - basis.computeBasis(gp, xx, uu, uu_dot); - - // Loop over Nodes in Element - for (int i = 0; i < 2; i++) - { - int row = x_ghosted_map_->GID(ne + i); - // printf("Proc=%d GlobalRow=%d LocalRow=%d Owned=%d\n", - // MyPID, row, ne+i,x_owned_map_.MyGID(row)); - if (x_owned_map_->MyGID(row)) - { - if (nonnull(f)) - { - (*f)[x_owned_map_->LID(x_ghosted_map_->GID(ne + i))] - += +basis.wt * basis.dz - * (basis.uu_dot * basis.phi[i] // transient - + (a_ / basis.dz * basis.duu - * basis.phi[i] // convection - + 1.0 / (basis.dz * basis.dz)) - * basis.duu - * basis.dphide[i] // diffusion - + k_ * basis.uu * basis.uu - * basis.phi[i]); // source - } - } - // Loop over Trial Functions - if (nonnull(J)) - { - for (int j = 0; j < 2; j++) - { - if (x_owned_map_->MyGID(row)) - { - int column = x_ghosted_map_->GID(ne + j); - double jac - = basis.wt * basis.dz - * (alpha * basis.phi[i] - * basis.phi[j] // transient - + beta - * (+a_ / basis.dz - * basis.dphide[j] - * basis.phi[i] // convection - + (1.0 / (basis.dz * basis.dz)) - * basis.dphide[j] - * basis.dphide[i] // diffusion - + 2.0 * k_ * basis.uu - * basis.phi[j] - * basis.phi[i] // source - )); - ierr = J->SumIntoGlobalValues( - row, 1, &jac, &column); - } - } - } - if (nonnull(M_inv)) - { - for (int j = 0; j < 2; j++) - { - if (x_owned_map_->MyGID(row)) - { - int column = x_ghosted_map_->GID(ne + j); - // The prec will be the diagonal of J. No need - // to assemble the other entries - if (row == column) - { - double jac - = basis.wt * basis.dz - * (alpha * basis.phi[i] - * basis.phi[j] // transient - + beta - * (+a_ / basis.dz - * basis.dphide[j] - * basis.phi[i] // convection - + (1.0 - / (basis.dz * basis.dz)) - * basis.dphide[j] - * basis.dphide[i] // diffusion - + 2.0 * k_ * basis.uu - * basis.phi[j] - * basis.phi[i] // source - )); - ierr = M_inv->SumIntoGlobalValues( - row, 1, &jac, &column); - } - } - } - } - } - } - } - - // Insert Boundary Conditions and modify Jacobian and function (F) - // U(0)=1 - if (comm_->MyPID() == 0) - { - if (nonnull(f)) - (*f)[0] = 0.0; // Setting BC above and zero residual here works - // for x and xDot solves. - //(*f)[0]= u[0] - 1.0; // BC equation works for x solves. - if (nonnull(J)) - { - int column = 0; - double jac = 1.0; - ierr = J->ReplaceGlobalValues(0, 1, &jac, &column); - column = 1; - jac = 0.0; - ierr = J->ReplaceGlobalValues(0, 1, &jac, &column); - } - if (nonnull(M_inv)) - { - int column = 0; - double jac = 1.0; - ierr = M_inv->ReplaceGlobalValues(0, 1, &jac, &column); - column = 1; - jac = 0.0; - ierr = M_inv->ReplaceGlobalValues(0, 1, &jac, &column); - } - } - - if (nonnull(J)) - J->FillComplete(); - - if (nonnull(M_inv)) - { - // invert the Jacobian diagonal for the preconditioner - M_inv->ExtractDiagonalCopy(*J_diagonal_); - - for (int i = 0; i < J_diagonal_->MyLength(); ++i) - (*J_diagonal_)[i] = 1.0 / ((*J_diagonal_)[i]); - - M_inv->PutScalar(0.0); - M_inv->ReplaceDiagonalValues(*J_diagonal_); - M_inv->FillComplete(); - } - - TEUCHOS_ASSERT(ierr > -1); - } -} - -//==================================================================== -// Basis vector - -// Constructor -Basis::Basis() - : uu(0.0) - , zz(0.0) - , duu(0.0) - , eta(0.0) - , wt(0.0) - , dz(0.0) - , uu_dot(0.0) - , duu_dot(0.0) -{ - phi = new double[2]; - dphide = new double[2]; -} - -// Destructor -Basis::~Basis() -{ - delete[] phi; - delete[] dphide; -} - -// Calculates a linear 1D basis -void Basis::computeBasis(int gp, double* z, double* u, double* u_dot) -{ - int N = 2; - if (gp == 0) - { - eta = -1.0 / sqrt(3.0); - wt = 1.0; - } - if (gp == 1) - { - eta = 1.0 / sqrt(3.0); - wt = 1.0; - } - - // Calculate basis function and derivatives at nodel pts - phi[0] = (1.0 - eta) / 2.0; - phi[1] = (1.0 + eta) / 2.0; - dphide[0] = -0.5; - dphide[1] = 0.5; - - // Caculate basis function and derivative at GP. - dz = 0.5 * (z[1] - z[0]); - zz = 0.0; - uu = 0.0; - duu = 0.0; - uu_dot = 0.0; - duu_dot = 0.0; - for (int i = 0; i < N; i++) - { - zz += z[i] * phi[i]; - uu += u[i] * phi[i]; - duu += u[i] * dphide[i]; - if (u_dot) - { - uu_dot += u_dot[i] * phi[i]; - duu_dot += u_dot[i] * dphide[i]; - } - } - - return; -} - -} // namespace Test -} // namespace VertexCFD - -#endif diff --git a/src/observers/unit_test/CMakeLists.txt b/src/observers/unit_test/CMakeLists.txt deleted file mode 100644 index ae533f8..0000000 --- a/src/observers/unit_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - MPI - LIBS VertexCFD - NAMES - WriteMatrix - ) diff --git a/src/observers/unit_test/tstWriteMatrix.cpp b/src/observers/unit_test/tstWriteMatrix.cpp deleted file mode 100644 index 04a641a..0000000 --- a/src/observers/unit_test/tstWriteMatrix.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -class WriteMatrixTester : public ::testing::Test -{ - public: - void SetUp() - { - // Create an Epetra communicator - epetra_comm_ = Teuchos::rcp(new Epetra_MpiComm(MPI_COMM_WORLD)); - - // Set time steps where matrices will be written - write_steps_.resize(3); - write_steps_[0] = 1; - write_steps_[1] = 5; - write_steps_[2] = 19; - } - - void TearDown() {} - - void solve() { integrator_->advanceTime(); } - - void checkResults() const - { - for (int val : write_steps_) - { - // Test Jacobian matrix - std::string filename = "jacobian-"; - filename += std::to_string(val) + ".mtx"; - - Epetra_CrsMatrix* A; - bool transpose = false; - int ierr = EpetraExt::MatrixMarketFileToCrsMatrix( - filename.c_str(), *epetra_comm_, A, transpose); - - EXPECT_EQ(0, ierr); - int num_global_rows = 101; - EXPECT_EQ(num_global_rows, A->NumGlobalRows()); - EXPECT_EQ(num_global_rows, A->NumGlobalCols()); - EXPECT_EQ(301, A->NumGlobalNonzeros()); - - // Check matrix values. Except for boundaries, the stencil is - // essentially [-100 200 100] to within a couple of digits. - // The problem is essentially linear - // and static, so all matrices will be nearly the same. - int* col_indices; - double* values; - int num_entries; - int num_local_rows = A->NumMyRows(); - int global_row; - for (int local_row = 0; local_row < num_local_rows; ++local_row) - { - A->ExtractMyRowView( - local_row, num_entries, values, col_indices); - global_row = A->GRID(local_row); - for (int entry = 0; entry < num_entries; ++entry) - { - int global_col = A->GCID(col_indices[entry]); - if (global_row == 0) - { - EXPECT_EQ(2, num_entries); - if (global_col == 0) - { - // Entry (0, 0) - EXPECT_NEAR(1.0, values[entry], 1e-6); - } - else - { - // Entry (0, 1) - EXPECT_DOUBLE_EQ(0.0, values[entry]); - } - } - else if (global_row == (num_global_rows - 1)) - { - EXPECT_EQ(2, num_entries); - if (global_col == (num_global_rows - 1)) - { - // Diagonal entry in last row - EXPECT_NEAR(99.5, values[entry], 0.1); - } - else - { - // Off-diagonal entry in last row - EXPECT_NEAR(-99.5, values[entry], 0.1); - } - } - else - { - EXPECT_EQ(3, num_entries); - if (global_col == global_row) - { - // Diagonal entry - EXPECT_NEAR(198., values[entry], 0.1); - } - else if (global_col == (global_row - 1)) - { - // Left of diagonal - EXPECT_NEAR(-99.5, values[entry], 0.1); - } - else if (global_col == (global_row + 1)) - { - // Right of diagonal entry - EXPECT_NEAR(-98.5, values[entry], 0.1); - } - } - } - } - - // Test Residual - filename = "residual-"; - filename += std::to_string(val) + ".mtx"; - Epetra_MultiVector* resid; - int err = EpetraExt::MatrixMarketFileToMultiVector( - filename.c_str(), A->RowMap(), resid); - EXPECT_EQ(0, err); - int global_length = 101; - EXPECT_EQ(global_length, resid->GlobalLength()); - std::vector minval(1), maxval(1), meanval(1); - resid->MinValue(minval.data()); - resid->MaxValue(maxval.data()); - resid->MeanValue(meanval.data()); - if (val == 1) - { - EXPECT_NEAR(-3.76e-8, minval[0], 1e-10); - EXPECT_NEAR(1.48e-8, maxval[0], 1e-10); - EXPECT_NEAR(1.49e-9, meanval[0], 1e-11); - } - else if (val == 5) - { - EXPECT_NEAR(-4.66e-8, minval[0], 1e-10); - EXPECT_NEAR(1.86e-8, maxval[0], 1e-10); - EXPECT_NEAR(3.94e-9, meanval[0], 1e-11); - } - else if (val == 19) - { - EXPECT_NEAR(-7.04e-14, minval[0], 1e-15); - EXPECT_EQ(0, maxval[0]); - EXPECT_NEAR(-3.29e-14, meanval[0], 1e-16); - } - - // We must call delete on EpetraExt-allocated object - delete A; - delete resid; - } - } - - protected: - void buildEvaluator() - { - // Set parameters for convection-diffusion-reaction model - int num_elements = 100; - double zmin = 0.0; - double zmax = 1.0; - double a = 1.0; // convection coefficient - double k = 2.0; // source - auto cdr_model = Teuchos::rcp(new CDR_Model( - epetra_comm_, num_elements, zmin, zmax, a, k)); - - // CDR model doesn't build its own linear solver, need to supply - // factory - Stratimikos::DefaultLinearSolverBuilder builder; - - auto stratimikos_params = Teuchos::parameterList(); - stratimikos_params->set("Linear Solver Type", "Belos"); - stratimikos_params->set("Preconditioner Type", "None"); - builder.setParameterList(stratimikos_params); - - Teuchos::RCP> lows_factory - = builder.createLinearSolveStrategy(""); - - cdr_model->set_W_factory(lows_factory); - model_ = cdr_model; - } - - void buildIntegrator() - { - // Set Tempus parameters - auto tempus_params = Teuchos::parameterList("Tempus"); - tempus_params->set("Integrator Name", "Default Integrator"); - - auto integrator_params - = Teuchos::sublist(tempus_params, "Default Integrator"); - integrator_params->set("Integrator Type", "Integrator Basic"); - integrator_params->set("Stepper Name", "Default Stepper"); - - auto control_params - = Teuchos::sublist(integrator_params, "Time Step Control"); - control_params->set("Initial Time", 0.); - control_params->set("Final Time", 20.); - control_params->set("Initial Time Step", 1.); - control_params->set("Minimum Time Step", 1.); - control_params->set("Maximum Time Step", 1.); - - auto stepper_params - = Teuchos::sublist(tempus_params, "Default Stepper"); - stepper_params->set("Stepper Type", "Backward Euler"); - - // Linear solver - stepper_params->set("Solver Name", "Default Solver"); - auto solver_params = Teuchos::sublist(stepper_params, "Default Solver"); - auto nox_params = Teuchos::sublist(solver_params, "NOX"); - nox_params->set( - "Output Information", - NOX::Utils::StepperIteration + NOX::Utils::StepperDetails); - - // Setup time integrator. -#if TRILINOS_MAJOR_MINOR_VERSION >= 130100 - integrator_ - = Tempus::createIntegratorBasic(tempus_params, model_); -#else - integrator_ = Tempus::integratorBasic(tempus_params, model_); -#endif - - // Build observer to write matrix to file - auto write_params = Teuchos::parameterList(); - write_params->set("Write Matrix", true); - write_params->set("Write Residual", true); - write_params->set("Write Steps", write_steps_); - auto matrix_writer = Teuchos::rcp( - new VertexCFD::TempusObserver::WriteMatrix(*write_params)); - integrator_->setObserver(matrix_writer); - - // Initialize integrator - integrator_->initialize(); - } - - Teuchos::RCP epetra_comm_; - Teuchos::RCP> model_; - Teuchos::RCP> integrator_; - Teuchos::Array write_steps_; -}; - -//---------------------------------------------------------------------------// -TEST_F(WriteMatrixTester, cdr_test) -{ - // This test only has 100 degrees of freedom in the system and bogs down - // if run on a large number of processors. - int num_procs; - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - if (num_procs > 16) - return; - - this->buildEvaluator(); - this->buildIntegrator(); - this->solve(); - this->checkResults(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/parameters/VertexCFD_GeneralScalarParameter.cpp b/src/parameters/VertexCFD_GeneralScalarParameter.cpp deleted file mode 100644 index ea98265..0000000 --- a/src/parameters/VertexCFD_GeneralScalarParameter.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_GeneralScalarParameter.hpp" -#include "VertexCFD_GeneralScalarParameter_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL( - VertexCFD::Parameter::GeneralScalarParameter) diff --git a/src/parameters/VertexCFD_GeneralScalarParameter.hpp b/src/parameters/VertexCFD_GeneralScalarParameter.hpp deleted file mode 100644 index a1ad8bc..0000000 --- a/src/parameters/VertexCFD_GeneralScalarParameter.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef VERTEXCFD_GENERALSCALARPARAMETER_HPP -#define VERTEXCFD_GENERALSCALARPARAMETER_HPP - -#include "VertexCFD_GeneralScalarParameterInput.hpp" - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -class GeneralScalarParameter -{ - public: - using scalar_type = typename EvalType::ScalarT; - - GeneralScalarParameter(const std::string& name, - scalar_type& ref_to_parameter); - - const std::string& name() const; - - void - update(const panzer::Workset& workset, - const std::unordered_map>& - general_scalar_params); - - private: - std::string _name; - scalar_type& _ref_to_parameter; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -#endif // end VERTEXCFD_GENERALSCALARPARAMETER_HPP diff --git a/src/parameters/VertexCFD_GeneralScalarParameterInput.cpp b/src/parameters/VertexCFD_GeneralScalarParameterInput.cpp deleted file mode 100644 index 6876f47..0000000 --- a/src/parameters/VertexCFD_GeneralScalarParameterInput.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "VertexCFD_GeneralScalarParameterInput.hpp" - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -bool GeneralScalarParameterInput::operator==( - const GeneralScalarParameterInput& rhs) const -{ - return parameter_name == rhs.parameter_name; -} - -//---------------------------------------------------------------------------// -std::ostream& -operator<<(std::ostream& out, const GeneralScalarParameterInput& input) -{ - out << input.parameter_name; - return out; -} - -//---------------------------------------------------------------------------// -std::istream& operator>>(std::istream& in, GeneralScalarParameterInput& input) -{ - std::getline(in, input.parameter_name); - return in; -} - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD diff --git a/src/parameters/VertexCFD_GeneralScalarParameterInput.hpp b/src/parameters/VertexCFD_GeneralScalarParameterInput.hpp deleted file mode 100644 index 4c1eefe..0000000 --- a/src/parameters/VertexCFD_GeneralScalarParameterInput.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef VERTEXCFD_GENERALSCALARPARAMTERINPUT_HPP -#define VERTEXCFD_GENERALSCALARPARAMTERINPUT_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -struct GeneralScalarParameterInput -{ - std::string parameter_name; - - bool operator==(const GeneralScalarParameterInput& rhs) const; -}; - -//---------------------------------------------------------------------------// -std::ostream& -operator<<(std::ostream& out, const GeneralScalarParameterInput& input); - -std::istream& operator>>(std::istream& in, GeneralScalarParameterInput& input); - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -//---------------------------------------------------------------------------// -namespace Teuchos -{ -template<> -class TypeNameTraits -{ - public: - static std::string name() { return "GeneralScalarParameter"; } -}; - -} // end namespace Teuchos - -//---------------------------------------------------------------------------// - -#endif // end VERTEXCFD_GENERALSCALARPARAMTERINPUT_HPP diff --git a/src/parameters/VertexCFD_GeneralScalarParameter_impl.hpp b/src/parameters/VertexCFD_GeneralScalarParameter_impl.hpp deleted file mode 100644 index 97c2f80..0000000 --- a/src/parameters/VertexCFD_GeneralScalarParameter_impl.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef VERTEXCFD_GENERALSCALARPARAMETER_IMPL_HPP -#define VERTEXCFD_GENERALSCALARPARAMETER_IMPL_HPP - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -GeneralScalarParameter::GeneralScalarParameter( - const std::string& name, scalar_type& ref_to_parameter) - : _name(name) - , _ref_to_parameter(ref_to_parameter) -{ -} - -//---------------------------------------------------------------------------// -template -const std::string& GeneralScalarParameter::name() const -{ - return _name; -} - -//---------------------------------------------------------------------------// -template -void GeneralScalarParameter::update( - const panzer::Workset& workset, - const std::unordered_map>& - general_scalar_params) -{ - // Lookup parameter values. - auto param_values = general_scalar_params.find(_name); - if (param_values == general_scalar_params.end()) - { - std::string msg = "GeneralScalar parameter " + _name + " not found"; - throw std::runtime_error(msg); - } - - // Check to see if this is an element block we have a value for. - auto block_name = workset.getElementBlock(); - auto block_value = param_values->second.find(block_name); - - // If the block is in the parameter list assign the value. - if (block_value != param_values->second.end()) - { - _ref_to_parameter = block_value->second; - } - - // Othwerwise just assign the default as this block wasn't given a - // specific value. - else - { - auto default_value = param_values->second.find("Default Value"); - if (default_value != param_values->second.end()) - { - _ref_to_parameter = default_value->second; - } - else - { - std::string msg = "GeneralScalar parameter " + _name - + " does not have a value for block " + block_name - + " and is also missing a default value"; - throw std::runtime_error(msg); - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -#endif // end VERTEXCFD_GENERALSCALARPARAMETER_IMPL_HPP diff --git a/src/parameters/VertexCFD_ParameterDatabase.cpp b/src/parameters/VertexCFD_ParameterDatabase.cpp deleted file mode 100644 index f1672ce..0000000 --- a/src/parameters/VertexCFD_ParameterDatabase.cpp +++ /dev/null @@ -1,357 +0,0 @@ -#include "VertexCFD_ParameterDatabase.hpp" -#include "VertexCFD_GeneralScalarParameterInput.hpp" -#include "VertexCFD_ScalarParameterInput.hpp" - -#include -#include -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -ParameterDatabase::ParameterDatabase( - const Teuchos::RCP>& comm) - : _comm(comm) -{ - // Create sublists. - _mesh_params = Teuchos::parameterList(); - _physics_params = Teuchos::parameterList(); - _scalar_params = Teuchos::parameterList(); - _general_scalar_params = Teuchos::parameterList(); - _block_mapping_params = Teuchos::parameterList(); - _bc_params = Teuchos::parameterList(); - _ic_params = Teuchos::parameterList(); - _closure_params = Teuchos::parameterList(); - _response_output_params = Teuchos::parameterList(); - _user_params = Teuchos::parameterList(); - _output_params = Teuchos::parameterList(); - _read_restart_params = Teuchos::parameterList(); - _write_restart_params = Teuchos::parameterList(); - _write_matrix_params = Teuchos::parameterList(); - _transient_solver_params = Teuchos::parameterList(); - _linear_solver_params = Teuchos::parameterList(); - - // Store the MPI communicator in the "User Data" ParameterList. - _user_params->set>>("Comm", _comm); -} - -//---------------------------------------------------------------------------// -ParameterDatabase::ParameterDatabase( - const Teuchos::RCP>& comm, - const Teuchos::RCP& parameters) - : _comm(comm) - , _input_params(parameters) -{ - // Get the sublists. - extractSublists(); -} - -//---------------------------------------------------------------------------// -ParameterDatabase::ParameterDatabase( - const Teuchos::RCP>& comm, - const std::string& xml_file) - : _comm(comm) -{ - // Parse file. - readParameterFile(xml_file); -} - -//---------------------------------------------------------------------------// -ParameterDatabase::ParameterDatabase( - const Teuchos::RCP>& comm, - int argc, - char* argv[]) - : _comm(comm) -{ - // Get the input file from the command line arguments. - Teuchos::CommandLineProcessor clp; - std::string input_file = "input.xml"; - clp.setOption("i", &input_file, "XML Input File"); - clp.parse(argc, argv, &std::cerr); - - // Parse file. - readParameterFile(input_file); -} - -//---------------------------------------------------------------------------// -void ParameterDatabase::readParameterFile(const std::string& xml_file) -{ - // Add custom input types. - TEUCHOS_ADD_TYPE_CONVERTER(ScalarParameterInput); - TEUCHOS_ADD_TYPE_CONVERTER(GeneralScalarParameterInput); - - // Build a parameter list from the inputs - _input_params = Teuchos::parameterList("Input Parameters"); - Teuchos::updateParametersFromXmlFileAndBroadcast( - xml_file, _input_params.ptr(), *_comm); - - // Get the sublists. - extractSublists(); -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::requiredSublist(const std::string& name) -{ - if (!_input_params->isSublist(name)) - { - std::string msg = "Sublist " + name + " missing from input"; - throw std::logic_error(msg); - } - return Teuchos::parameterList(_input_params->sublist(name)); -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::optionalSublist(const std::string& name) -{ - if (_input_params->isSublist(name)) - { - return Teuchos::parameterList(_input_params->sublist(name)); - } - else - { - return Teuchos::null; - } -} - -//---------------------------------------------------------------------------// -void ParameterDatabase::extractSublists() -{ - // Check if we are using new input format. This will be removed once - // the transition to the new format is complete. - if (_input_params->isParameter("Use New Input Format")) - { - _use_new_input = _input_params->get("Use New Input Format"); - } - - // Create sublists from new format - if (_use_new_input) - { - extractSublistsNew(); - } - - // Get sublists directly in old format. - else - { - extractSublistsOld(); - } - - // Store the MPI communicator in the "User Data" ParameterList. - _user_params->set>>("Comm", _comm); -} - -//---------------------------------------------------------------------------// -void ParameterDatabase::extractSublistsOld() -{ - // Get required sublists. - _mesh_params = requiredSublist("Mesh"); - _physics_params = requiredSublist("Physics Blocks"); - _block_mapping_params = requiredSublist("Block ID to Physics ID Mapping"); - _bc_params = requiredSublist("Boundary Conditions"); - _ic_params = requiredSublist("Initial Conditions"); - _closure_params = requiredSublist("Closure Models"); - _user_params = requiredSublist("User Data"); - _transient_solver_params = requiredSublist("Tempus"); - _linear_solver_params = requiredSublist("Linear Solver"); - - // Get optional sublists. - _profiling_params = optionalSublist("Profiling"); - _scalar_params = optionalSublist("Scalar Parameters"); - _general_scalar_params = optionalSublist("General Scalar Parameters"); - _output_params = optionalSublist("Solution Output"); - _response_output_params = optionalSublist("Scalar Response Output"); - if (Teuchos::is_null(_response_output_params)) - { - // Check old name for backward compatibility - _response_output_params = optionalSublist("Volume Integral Output"); - } - _read_restart_params = optionalSublist("Read Restart"); - _write_restart_params = optionalSublist("Write Restart"); - _write_matrix_params = optionalSublist("Write Matrix"); -} - -//---------------------------------------------------------------------------// -void ParameterDatabase::extractSublistsNew() -{ - // Get required sublists. - _mesh_params = requiredSublist("Mesh"); - _assembly_params = requiredSublist("Equation Assembly"); - _bc_params = requiredSublist("Boundary Conditions"); - _ic_params = requiredSublist("Initial Conditions"); - _closure_params = requiredSublist("Closure Models"); - _user_params = requiredSublist("User Data"); - _transient_solver_params = requiredSublist("Tempus"); - _linear_solver_params = requiredSublist("Linear Solver"); - - // Get optional sublists. - _profiling_params = optionalSublist("Profiling"); - _scalar_params = optionalSublist("Scalar Parameters"); - _general_scalar_params = optionalSublist("General Scalar Parameters"); - _output_params = optionalSublist("Solution Output"); - _response_output_params = optionalSublist("Scalar Response Output"); - if (Teuchos::is_null(_response_output_params)) - { - // Check old name for backward compatibility - _response_output_params = optionalSublist("Volume Integral Output"); - } - _read_restart_params = optionalSublist("Read Restart"); - _write_restart_params = optionalSublist("Write Restart"); - _write_matrix_params = optionalSublist("Write Matrix"); - - // Make auxiliary parameter lists. - _physics_params - = Teuchos::parameterList(_input_params->sublist("Physics Blocks")); - _block_mapping_params = Teuchos::parameterList( - _input_params->sublist("Block ID to Physics ID Mapping")); -} - -//---------------------------------------------------------------------------// -Teuchos::RCP> ParameterDatabase::comm() const -{ - return _comm; -} - -//---------------------------------------------------------------------------// -// Main list accessor. -Teuchos::RCP ParameterDatabase::allParameters() const -{ - return _input_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP ParameterDatabase::meshParameters() const -{ - return _mesh_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::assemblyParameters() const -{ - return _assembly_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::physicsParameters() const -{ - return _physics_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP ParameterDatabase::scalarParameters() const -{ - return _scalar_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::generalScalarParameters() const -{ - return _general_scalar_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::blockMappingParameters() const -{ - return _block_mapping_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::boundaryConditionParameters() const -{ - return _bc_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::initialConditionParameters() const -{ - return _ic_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::closureModelParameters() const -{ - return _closure_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::responseOutputParameters() const -{ - return _response_output_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP ParameterDatabase::userParameters() const -{ - return _user_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP ParameterDatabase::outputParameters() const -{ - return _output_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::readRestartParameters() const -{ - return _read_restart_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::writeRestartParameters() const -{ - return _write_restart_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::writeMatrixParameters() const -{ - return _write_matrix_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::profilingParameters() const -{ - return _profiling_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::transientSolverParameters() const -{ - return _transient_solver_params; -} - -//---------------------------------------------------------------------------// -Teuchos::RCP -ParameterDatabase::linearSolverParameters() const -{ - return _linear_solver_params; -} - -//---------------------------------------------------------------------------// -bool ParameterDatabase::useNewInputFormat() const -{ - return _use_new_input; -} - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -//---------------------------------------------------------------------------// diff --git a/src/parameters/VertexCFD_ParameterDatabase.hpp b/src/parameters/VertexCFD_ParameterDatabase.hpp deleted file mode 100644 index 969958e..0000000 --- a/src/parameters/VertexCFD_ParameterDatabase.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef VERTEXCFD_PARAMETERDATABASE_HPP -#define VERTEXCFD_PARAMETERDATABASE_HPP - -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -class ParameterDatabase -{ - public: - // Default constructor. - ParameterDatabase(const Teuchos::RCP>& comm); - - // Parameter list constructor. - ParameterDatabase(const Teuchos::RCP>& comm, - const Teuchos::RCP& parameters); - - // XML file constructor. - ParameterDatabase(const Teuchos::RCP>& comm, - const std::string& xml_file); - - // Main argument constructor. - ParameterDatabase(const Teuchos::RCP>& comm, - int argc, - char* argv[]); - - // Communicator. - Teuchos::RCP> comm() const; - - // Main list accessor. - Teuchos::RCP allParameters() const; - - // Sublist accessors. - Teuchos::RCP meshParameters() const; - Teuchos::RCP assemblyParameters() const; - Teuchos::RCP scalarParameters() const; - Teuchos::RCP generalScalarParameters() const; - Teuchos::RCP boundaryConditionParameters() const; - Teuchos::RCP initialConditionParameters() const; - Teuchos::RCP closureModelParameters() const; - Teuchos::RCP responseOutputParameters() const; - Teuchos::RCP userParameters() const; - Teuchos::RCP outputParameters() const; - Teuchos::RCP readRestartParameters() const; - Teuchos::RCP writeRestartParameters() const; - Teuchos::RCP writeMatrixParameters() const; - Teuchos::RCP profilingParameters() const; - Teuchos::RCP transientSolverParameters() const; - Teuchos::RCP linearSolverParameters() const; - - // Deprecated sublist accessors. - Teuchos::RCP physicsParameters() const; - Teuchos::RCP blockMappingParameters() const; - - // New format boolean. This will be removed once the transition to the new - // format is complete. - bool useNewInputFormat() const; - - private: - // Read an xml file with parameters and extract sublists. - void readParameterFile(const std::string& xml_file); - - // Get the sublists from the input parameters. - void extractSublists(); - - // Get the sublists from the input parameters - old variant. (Deprecated) - void extractSublistsOld(); - - // Get the sublists from the input parameters - new variant. - void extractSublistsNew(); - - // Get a required sublist from the main input list. - Teuchos::RCP - requiredSublist(const std::string& name); - - // Get an optional sublist from the main input list. - Teuchos::RCP - optionalSublist(const std::string& name); - - private: - Teuchos::RCP> _comm; - Teuchos::RCP _input_params; - Teuchos::RCP _mesh_params; - Teuchos::RCP _assembly_params; - Teuchos::RCP _scalar_params; - Teuchos::RCP _general_scalar_params; - Teuchos::RCP _bc_params; - Teuchos::RCP _ic_params; - Teuchos::RCP _closure_params; - Teuchos::RCP _response_output_params; - Teuchos::RCP _user_params; - Teuchos::RCP _output_params; - Teuchos::RCP _read_restart_params; - Teuchos::RCP _write_restart_params; - Teuchos::RCP _write_matrix_params; - Teuchos::RCP _profiling_params; - Teuchos::RCP _transient_solver_params; - Teuchos::RCP _linear_solver_params; - bool _use_new_input = false; - - // Deprecated lists. - Teuchos::RCP _physics_params; - Teuchos::RCP _block_mapping_params; -}; - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_PARAMETERDATABASE_HPP diff --git a/src/parameters/VertexCFD_ScalarParameter.cpp b/src/parameters/VertexCFD_ScalarParameter.cpp deleted file mode 100644 index 04e12ba..0000000 --- a/src/parameters/VertexCFD_ScalarParameter.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_ScalarParameter.hpp" -#include "VertexCFD_ScalarParameter_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL(VertexCFD::Parameter::ScalarParameter) diff --git a/src/parameters/VertexCFD_ScalarParameter.hpp b/src/parameters/VertexCFD_ScalarParameter.hpp deleted file mode 100644 index 895aca9..0000000 --- a/src/parameters/VertexCFD_ScalarParameter.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETER_HPP -#define VERTEXCFD_SCALARPARAMETER_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -// Global scalar parameter. Parameters managed by this class will be -// updated from the parameter library. -//---------------------------------------------------------------------------// -template -class ScalarParameter -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ScalarParameter(const std::string& name, scalar_type& ref_to_parameter); - - const std::string& name() const; - - void update(const panzer::GlobalData& global_data); - - private: - std::string _name; - scalar_type& _ref_to_parameter; -}; - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETER_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterEvaluator.cpp b/src/parameters/VertexCFD_ScalarParameterEvaluator.cpp deleted file mode 100644 index bf0d169..0000000 --- a/src/parameters/VertexCFD_ScalarParameterEvaluator.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_ScalarParameterEvaluator.hpp" -#include "VertexCFD_ScalarParameterEvaluator_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::Parameter::ScalarParameterEvaluator) diff --git a/src/parameters/VertexCFD_ScalarParameterEvaluator.hpp b/src/parameters/VertexCFD_ScalarParameterEvaluator.hpp deleted file mode 100644 index 5ff2174..0000000 --- a/src/parameters/VertexCFD_ScalarParameterEvaluator.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETEREVALUATOR_HPP -#define VERTEXCFD_SCALARPARAMETEREVALUATOR_HPP - -#include "VertexCFD_ScalarParameterManager.hpp" - -#include -#include - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -class ScalarParameterEvaluator : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - ScalarParameterEvaluator( - const Teuchos::RCP>& param_manager, - const Teuchos::RCP& global_data); - - void evaluateFields(typename Traits::EvalData workset) override; - - private: - Teuchos::RCP _param_update_trigger; - Teuchos::RCP> _param_manager; - Teuchos::RCP _global_data; -}; - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETEREVALUATOR_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterEvaluator_impl.hpp b/src/parameters/VertexCFD_ScalarParameterEvaluator_impl.hpp deleted file mode 100644 index 23e99d1..0000000 --- a/src/parameters/VertexCFD_ScalarParameterEvaluator_impl.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETEREVALUATOR_IMPL_HPP -#define VERTEXCFD_SCALARPARAMETEREVALUATOR_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -ScalarParameterEvaluator::ScalarParameterEvaluator( - const Teuchos::RCP>& param_manager, - const Teuchos::RCP& global_data) - : _param_manager(param_manager) - , _global_data(global_data) -{ - auto dummy_layout = Teuchos::rcp(new PHX::MDALayout(0)); - _param_update_trigger = Teuchos::rcp( - new PHX::Tag("scalar_parameter_eval", dummy_layout)); - this->addEvaluatedField(*_param_update_trigger); - this->setName("Scalar Parameter Evaluation"); -} - -//---------------------------------------------------------------------------// -template -void ScalarParameterEvaluator::evaluateFields( - typename Traits::EvalData workset) -{ - _param_manager->update(*_global_data, workset); -} - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETEREVALUATOR_IMPL_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterInput.cpp b/src/parameters/VertexCFD_ScalarParameterInput.cpp deleted file mode 100644 index 1507b40..0000000 --- a/src/parameters/VertexCFD_ScalarParameterInput.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "VertexCFD_ScalarParameterInput.hpp" - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -bool ScalarParameterInput::operator==(const ScalarParameterInput& rhs) const -{ - return parameter_name == rhs.parameter_name; -} - -//---------------------------------------------------------------------------// -std::ostream& operator<<(std::ostream& out, const ScalarParameterInput& input) -{ - out << input.parameter_name; - return out; -} - -//---------------------------------------------------------------------------// -std::istream& operator>>(std::istream& in, ScalarParameterInput& input) -{ - std::getline(in, input.parameter_name); - return in; -} - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD diff --git a/src/parameters/VertexCFD_ScalarParameterInput.hpp b/src/parameters/VertexCFD_ScalarParameterInput.hpp deleted file mode 100644 index c93970a..0000000 --- a/src/parameters/VertexCFD_ScalarParameterInput.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETERINPUT_HPP -#define VERTEXCFD_SCALARPARAMETERINPUT_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -struct ScalarParameterInput -{ - std::string parameter_name; - - bool operator==(const ScalarParameterInput& rhs) const; -}; - -//---------------------------------------------------------------------------// -std::ostream& operator<<(std::ostream& out, const ScalarParameterInput& input); - -std::istream& operator>>(std::istream& in, ScalarParameterInput& input); - -//---------------------------------------------------------------------------// - -} // end namespace Parameter -} // end namespace VertexCFD - -//---------------------------------------------------------------------------// -namespace Teuchos -{ -template<> -class TypeNameTraits -{ - public: - static std::string name() { return "ScalarParameter"; } -}; - -} // end namespace Teuchos - -//---------------------------------------------------------------------------// - -#endif // end VERTEXCFD_SCALARPARAMETERINPUT_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterManager.cpp b/src/parameters/VertexCFD_ScalarParameterManager.cpp deleted file mode 100644 index defacde..0000000 --- a/src/parameters/VertexCFD_ScalarParameterManager.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_ScalarParameterManager.hpp" -#include "VertexCFD_ScalarParameterManager_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL( - VertexCFD::Parameter::ScalarParameterManager) diff --git a/src/parameters/VertexCFD_ScalarParameterManager.hpp b/src/parameters/VertexCFD_ScalarParameterManager.hpp deleted file mode 100644 index ba84b6a..0000000 --- a/src/parameters/VertexCFD_ScalarParameterManager.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETERMANAGER_HPP -#define VERTEXCFD_SCALARPARAMETERMANAGER_HPP - -#include "VertexCFD_ParameterDatabase.hpp" -#include "VertexCFD_ScalarParameterObserver.hpp" - -#include - -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -class ScalarParameterManager -{ - public: - // Construct a manager from the parameter database. - ScalarParameterManager(const ParameterDatabase& parameter_db); - - // Assign a new observer to be managed by this manager. - void - addObserver(const Teuchos::RCP>& observer); - - // Update the parameters in all observers owned by this manager with the - // given global data. Also update the state of all observers with the new - // parameter values. - void update(const panzer::GlobalData& global_data, - const panzer::Workset& workset); - - private: - std::vector>> _observers; - std::unordered_map> - _general_parameter_data; -}; - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETERMANAGER_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterManager_impl.hpp b/src/parameters/VertexCFD_ScalarParameterManager_impl.hpp deleted file mode 100644 index 71e9c3b..0000000 --- a/src/parameters/VertexCFD_ScalarParameterManager_impl.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETERMANAGER_IMPL_HPP -#define VERTEXCFD_SCALARPARAMETERMANAGER_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -ScalarParameterManager::ScalarParameterManager( - const ParameterDatabase& parameter_db) -{ - // Extract the general_scalar parameters from the input. - auto general_scalar_params = parameter_db.generalScalarParameters(); - if (Teuchos::nonnull(general_scalar_params)) - { - // Loop through parameters - for (const auto& gp : *general_scalar_params) - { - // Loop through block values - std::unordered_map block_values; - auto param_values = general_scalar_params->sublist(gp.first); - for (const auto& bv : param_values) - { - // Insert block name and value. - block_values.emplace(bv.first, bv.second.getValue(0)); - } - - // Insert parameter name and block values. - _general_parameter_data.emplace(gp.first, std::move(block_values)); - } - } -} - -//---------------------------------------------------------------------------// -template -void ScalarParameterManager::addObserver( - const Teuchos::RCP>& observer) -{ - _observers.emplace_back(observer); -} - -//---------------------------------------------------------------------------// -template -void ScalarParameterManager::update( - const panzer::GlobalData& global_data, const panzer::Workset& workset) -{ - for (auto& o : _observers) - { - o->update(global_data, workset, _general_parameter_data); - } -} - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETERMANAGER_IMPL_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterObserver.cpp b/src/parameters/VertexCFD_ScalarParameterObserver.cpp deleted file mode 100644 index 6df41db..0000000 --- a/src/parameters/VertexCFD_ScalarParameterObserver.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_ScalarParameterObserver.hpp" -#include "VertexCFD_ScalarParameterObserver_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL( - VertexCFD::Parameter::ScalarParameterObserver) diff --git a/src/parameters/VertexCFD_ScalarParameterObserver.hpp b/src/parameters/VertexCFD_ScalarParameterObserver.hpp deleted file mode 100644 index db6bca0..0000000 --- a/src/parameters/VertexCFD_ScalarParameterObserver.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETEROBSERVER_HPP -#define VERTEXCFD_SCALARPARAMETEROBSERVER_HPP - -#include "VertexCFD_GeneralScalarParameter.hpp" -#include "VertexCFD_ScalarParameter.hpp" - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -class ScalarParameterObserver -{ - public: - using scalar_type = typename EvalType::ScalarT; - - // Register a parameter with the observer. The parameter will be extracted - // from the parameter list and assigned to the reference (note that this - // reference is stored by the underlying parameter). If the parameter is - // not in the list the provided default value will be used. - void registerParameter(const std::string& name, - const double default_value, - const Teuchos::ParameterList& plist, - scalar_type& ref_to_parameter); - - // Update the value of all parameters owned by the observer with the - // current state of the global data and the current workset. - void - update(const panzer::GlobalData& global_data, - const panzer::Workset& workset, - const std::unordered_map>& - general_parameter_data); - - protected: - // After the parameters have been updated, update the state of the - // observer as necessary to be consistent with the new parameter - // values. This will always be called inside of update() and therefore the - // parameter state is guaranteed to be up-to-date. - virtual void updateStateWithNewParameters() = 0; - - private: - std::vector> _scalar_parameters; - std::vector> _general_parameters; -}; - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETEROBSERVER_HPP diff --git a/src/parameters/VertexCFD_ScalarParameterObserver_impl.hpp b/src/parameters/VertexCFD_ScalarParameterObserver_impl.hpp deleted file mode 100644 index dc76d28..0000000 --- a/src/parameters/VertexCFD_ScalarParameterObserver_impl.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETEROBSERVER_IMPL_HPP -#define VERTEXCFD_SCALARPARAMETEROBSERVER_IMPL_HPP - -#include "VertexCFD_GeneralScalarParameterInput.hpp" -#include "VertexCFD_ScalarParameterInput.hpp" - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -void ScalarParameterObserver::registerParameter( - const std::string& name, - const double default_value, - const Teuchos::ParameterList& plist, - scalar_type& ref_to_parameter) -{ - // Sensitivity Parameter - // General Parameter - - // Sensitivity scalar parameter. Initially assign the default - // value. Parameterized values will get assigned during update(), - // including those nominal values set in the input file. Sensitivity - // scalar parameters have: - // - // nominal value: provided by user, applied when model is not being - // parameterized w.r.t. given parameter - // - // default value: provided by developer, applied when the user - // doesn't specify the parameter in any way - if (plist.isType(name)) - { - auto param_input = plist.get(name); - _scalar_parameters.emplace_back(param_input.parameter_name, - ref_to_parameter); - ref_to_parameter = default_value; - } - - // General scalar parameter. A default value is provided to use for all - // blocks that aren't given their own parameter value. Block parameter - // values will get assigned during update(), including those default - // values set in the input file. General scalar parameters have: - // - // block value: provided by user, applied on the given block - // - // default value; provided by user, applied when no block value is - // given for a block - // - // default value: provided by developer, applied when the user - // doesn't specify the parameter in any way - else if (plist.isType(name)) - { - auto param_input = plist.get(name); - _general_parameters.emplace_back(param_input.parameter_name, - ref_to_parameter); - ref_to_parameter = default_value; - } - - // Check for local parameter. - else if (plist.isType(name)) - { - ref_to_parameter = plist.get(name); - } - - // Otherwise treat as local parameter and set default. - else - { - ref_to_parameter = default_value; - } -} - -//---------------------------------------------------------------------------// -template -void ScalarParameterObserver::update( - const panzer::GlobalData& global_data, - const panzer::Workset& workset, - const std::unordered_map>& - general_parameter_data) -{ - for (auto& p : _scalar_parameters) - { - p.update(global_data); - } - for (auto& p : _general_parameters) - { - p.update(workset, general_parameter_data); - } - this->updateStateWithNewParameters(); -} - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETEROBSERVER_IMPL_HPP diff --git a/src/parameters/VertexCFD_ScalarParameter_impl.hpp b/src/parameters/VertexCFD_ScalarParameter_impl.hpp deleted file mode 100644 index 295eaf9..0000000 --- a/src/parameters/VertexCFD_ScalarParameter_impl.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef VERTEXCFD_SCALARPARAMETER_IMPL_HPP -#define VERTEXCFD_SCALARPARAMETER_IMPL_HPP - -namespace VertexCFD -{ -namespace Parameter -{ -//---------------------------------------------------------------------------// -template -ScalarParameter::ScalarParameter(const std::string& name, - scalar_type& ref_to_parameter) - : _name(name) - , _ref_to_parameter(ref_to_parameter) -{ -} - -//---------------------------------------------------------------------------// -template -const std::string& ScalarParameter::name() const -{ - return _name; -} - -//---------------------------------------------------------------------------// -template -void ScalarParameter::update(const panzer::GlobalData& global_data) -{ - _ref_to_parameter = global_data.pl->getValue(_name); -} - -//---------------------------------------------------------------------------// - -} // namespace Parameter -} // namespace VertexCFD - -#endif // end VERTEXCFD_SCALARPARAMETER_IMPL_HPP diff --git a/src/parameters/unit_test/CMakeLists.txt b/src/parameters/unit_test/CMakeLists.txt deleted file mode 100644 index 512529e..0000000 --- a/src/parameters/unit_test/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -configure_file( - VertexCFD_ParameterUnitTestConfig.hpp.cmakein - VertexCFD_ParameterUnitTestConfig.hpp ) - -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - ParameterDatabase - GeneralScalarParameterInput - GeneralScalarParameter - ScalarParameter - ScalarParameterInput - ScalarParameterObserver - ScalarParameterEvaluator - ) diff --git a/src/parameters/unit_test/VertexCFD_ParameterUnitTestConfig.hpp.cmakein b/src/parameters/unit_test/VertexCFD_ParameterUnitTestConfig.hpp.cmakein deleted file mode 100644 index e1ad226..0000000 --- a/src/parameters/unit_test/VertexCFD_ParameterUnitTestConfig.hpp.cmakein +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef VERTEXCFD_PARAMETERUNITTESTCONFIG_HPP -#define VERTEXCFD_PARAMETERUNITTESTCONFIG_HPP - -constexpr char VERTEXCFD_PARAMETER_TEST_DATA_DIR[] = R"(@CMAKE_CURRENT_SOURCE_DIR@/data/)"; -constexpr char VERTEXCFD_RESPONSE_TEST_DATA_DIR[] = R"(@CMAKE_SOURCE_DIR@/src/responses/unit_test/data/)"; - -#endif // end VERTEXCFD_PARAMETERUNITTESTCONFIG_HPP diff --git a/src/parameters/unit_test/data/input_parser_test.xml b/src/parameters/unit_test/data/input_parser_test.xml deleted file mode 100644 index 18b5924..0000000 --- a/src/parameters/unit_test/data/input_parser_test.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/parameters/unit_test/data/scalar_parameter_evaluator_test.xml b/src/parameters/unit_test/data/scalar_parameter_evaluator_test.xml deleted file mode 100644 index ba85dc2..0000000 --- a/src/parameters/unit_test/data/scalar_parameter_evaluator_test.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/parameters/unit_test/tstGeneralScalarParameter.cpp b/src/parameters/unit_test/tstGeneralScalarParameter.cpp deleted file mode 100644 index be904ce..0000000 --- a/src/parameters/unit_test/tstGeneralScalarParameter.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "VertexCFD_ParameterUnitTestConfig.hpp" - -#include "parameters/VertexCFD_GeneralScalarParameter.hpp" - -#include -#include - -#include - -#include -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Residual::ScalarT& observed) -{ - return observed; -} - -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Jacobian::ScalarT& observed) -{ - return observed.val(); -} - -//---------------------------------------------------------------------------// -template -void testGeneralScalarParameter() -{ - // Create input structure. - std::unordered_map> - general_parameter_data; - { - std::unordered_map param_values; - param_values.emplace("Default Value", 1.2345); - param_values.emplace("Block 1", 2.3456); - general_parameter_data.emplace("Foo", std::move(param_values)); - } - { - general_parameter_data.emplace( - "Bar", std::unordered_map{}); - } - - // Create worksets. - panzer::Workset w1; - w1.block_id = "Block 1"; - panzer::Workset w2; - w2.block_id = "Block 2"; - - // Create a parameter. - typename EvalType::ScalarT result = -1.0; - Parameter::GeneralScalarParameter gsp("Foo", result); - EXPECT_EQ("Foo", gsp.name()); - - // Test evaluation. - gsp.update(w1, general_parameter_data); - EXPECT_EQ(2.3456, getValue(result)); - gsp.update(w2, general_parameter_data); - EXPECT_EQ(1.2345, getValue(result)); - - // Try a parameter not in the list. - Parameter::GeneralScalarParameter bad_param("Biz", result); - const std::string biz_msg = "GeneralScalar parameter Biz not found"; - EXPECT_THROW( - try { - bad_param.update(w1, general_parameter_data); - } catch (const std::runtime_error& e) { - EXPECT_EQ(biz_msg, e.what()); - throw; - }, - std::runtime_error); - - // Try a parameter without a default for a block that doesn't exist. - Parameter::GeneralScalarParameter no_default("Bar", result); - const std::string bar_msg - = "GeneralScalar parameter Bar" - " does not have a value for block Block 2" - " and is also missing a default value"; - EXPECT_THROW( - try { - no_default.update(w2, general_parameter_data); - } catch (const std::runtime_error& e) { - EXPECT_EQ(bar_msg, e.what()); - throw; - }, - std::runtime_error); -} - -//---------------------------------------------------------------------------// -TEST(GeneralScalarParameter, residual) -{ - testGeneralScalarParameter(); -} - -//---------------------------------------------------------------------------// -TEST(GeneralScalarParameter, jacobian) -{ - testGeneralScalarParameter(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/parameters/unit_test/tstGeneralScalarParameterInput.cpp b/src/parameters/unit_test/tstGeneralScalarParameterInput.cpp deleted file mode 100644 index 8a8be38..0000000 --- a/src/parameters/unit_test/tstGeneralScalarParameterInput.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "parameters/VertexCFD_GeneralScalarParameterInput.hpp" - -#include -#include -#include - -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -TEST(GeneralScalarParameterInput, xml_write_read) -{ - // Inject custom type for parameter input. - TEUCHOS_ADD_TYPE_CONVERTER(Parameter::GeneralScalarParameterInput); - - // Make a parameter list with a general_scalar parameter input. - Teuchos::ParameterList write_list; - Parameter::GeneralScalarParameterInput write_input = {"Parameter Name"}; - write_list.set("Object Parameter", write_input); - - // Write a parameter list to XML. Write it to cout too so we can see it. - Teuchos::writeParameterListToXmlFile( - write_list, "general_scalar_parameter_input_test.xml"); - Teuchos::writeParameterListToXmlOStream(write_list, std::cout); - - // Read the parameter list back in. Also write it to cout so we can see - // it. - auto read_list = Teuchos::getParametersFromXmlFile( - "general_scalar_parameter_input_test.xml"); - std::cout << *read_list; - - // Check result. - auto read_input = read_list->get( - "Object Parameter"); - EXPECT_EQ("Parameter Name", read_input.parameter_name); - - // Check equality operator. - Parameter::GeneralScalarParameterInput eq_check = {"Parameter Name"}; - EXPECT_EQ(eq_check, write_input); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/parameters/unit_test/tstParameterDatabase.cpp b/src/parameters/unit_test/tstParameterDatabase.cpp deleted file mode 100644 index d8faf95..0000000 --- a/src/parameters/unit_test/tstParameterDatabase.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include - -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -void testDefaultDatabase() -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Create database. - VertexCFD::Parameter::ParameterDatabase parameter_db(comm); - - // Check that all parameter lists got populated. - EXPECT_TRUE(Teuchos::nonnull(parameter_db.meshParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.physicsParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.blockMappingParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.scalarParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.generalScalarParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.boundaryConditionParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.initialConditionParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.closureModelParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.responseOutputParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.userParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.outputParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.readRestartParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.writeRestartParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.writeMatrixParameters())); - EXPECT_TRUE(Teuchos::is_null(parameter_db.profilingParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.transientSolverParameters())); - EXPECT_TRUE(Teuchos::nonnull(parameter_db.linearSolverParameters())); - - // Check that we added the communicator to the user data. - auto param_comm - = parameter_db.userParameters() - ->get>>("Comm"); - EXPECT_EQ(comm->getRank(), param_comm->getRank()); - EXPECT_EQ(comm->getSize(), param_comm->getSize()); -} - -//---------------------------------------------------------------------------// -TEST(ParameterDatabase, default_test) -{ - testDefaultDatabase(); -} - -//---------------------------------------------------------------------------// -void testInputParser(const Teuchos::RCP>& comm, - const VertexCFD::Parameter::ParameterDatabase& parameter_db) -{ - // Check that all parameter lists got populated. - EXPECT_DOUBLE_EQ(1.0, parameter_db.meshParameters()->get("value")); - EXPECT_DOUBLE_EQ(1.4, - parameter_db.physicsParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 1.3, parameter_db.blockMappingParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 1.6, parameter_db.boundaryConditionParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 1.7, parameter_db.initialConditionParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 1.8, parameter_db.closureModelParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.4, parameter_db.responseOutputParameters()->get("value")); - EXPECT_DOUBLE_EQ(1.5, parameter_db.userParameters()->get("value")); - EXPECT_DOUBLE_EQ(1.1, - parameter_db.outputParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.3, parameter_db.readRestartParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.2, parameter_db.writeRestartParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.1, parameter_db.writeMatrixParameters()->get("value")); - EXPECT_DOUBLE_EQ(1.2, - parameter_db.profilingParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.0, parameter_db.transientSolverParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 1.9, parameter_db.linearSolverParameters()->get("value")); - EXPECT_DOUBLE_EQ(2.5, - parameter_db.scalarParameters()->get("value")); - EXPECT_DOUBLE_EQ( - 2.6, parameter_db.generalScalarParameters()->get("value")); - - // Check the parameter communicator. - EXPECT_EQ(comm->getRank(), parameter_db.comm()->getRank()); - EXPECT_EQ(comm->getSize(), parameter_db.comm()->getSize()); - - // Check that we added the communicator to the user data. - auto param_comm - = parameter_db.userParameters() - ->get>>("Comm"); - EXPECT_EQ(comm->getRank(), param_comm->getRank()); - EXPECT_EQ(comm->getSize(), param_comm->getSize()); -} - -//---------------------------------------------------------------------------// -TEST(ParameterDatabase, argv_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - int argc = 2; - std::string option = "--i="; - std::string location = VERTEXCFD_PARAMETER_TEST_DATA_DIR; - std::string file = "input_parser_test.xml"; - std::string argv_str = option + location + file; - char* argv[2]; - argv[1] = &argv_str[0]; - VertexCFD::Parameter::ParameterDatabase parameter_db(comm, argc, argv); - - // Test - testInputParser(comm, parameter_db); -} - -//---------------------------------------------------------------------------// -TEST(ParameterDatabase, file_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - std::string location = VERTEXCFD_PARAMETER_TEST_DATA_DIR; - std::string file = "input_parser_test.xml"; - std::string filename = location + file; - VertexCFD::Parameter::ParameterDatabase parameter_db(comm, filename); - - // Test - testInputParser(comm, parameter_db); -} - -//---------------------------------------------------------------------------// -TEST(ParameterDatabase, list_test) -{ - // Get the MPI communicator. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - // Parse input. - std::string location = VERTEXCFD_PARAMETER_TEST_DATA_DIR; - std::string file = "input_parser_test.xml"; - std::string filename = location + file; - VertexCFD::Parameter::ParameterDatabase file_db(comm, filename); - - // Create a new parameter database from the input list. - VertexCFD::Parameter::ParameterDatabase parameter_db( - comm, file_db.allParameters()); - - // Test - testInputParser(comm, parameter_db); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/parameters/unit_test/tstScalarParameter.cpp b/src/parameters/unit_test/tstScalarParameter.cpp deleted file mode 100644 index a00e3ee..0000000 --- a/src/parameters/unit_test/tstScalarParameter.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "VertexCFD_ParameterUnitTestConfig.hpp" - -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "parameters/VertexCFD_ScalarParameter.hpp" - -#include "drivers/VertexCFD_InitialConditionManager.hpp" -#include "drivers/VertexCFD_MeshManager.hpp" -#include "drivers/VertexCFD_PhysicsManager.hpp" - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Residual::ScalarT& observed) -{ - return observed; -} - -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Jacobian::ScalarT& observed) -{ - return observed.val(); -} - -//---------------------------------------------------------------------------// -template -void testScalarParameter() -{ - // Setup base data structures to make a parameter library. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - const std::string location = VERTEXCFD_RESPONSE_TEST_DATA_DIR; - const std::string file = "response_manager_test.xml"; - auto parameter_db = Teuchos::rcp( - new Parameter::ParameterDatabase(comm, location + file)); - parameter_db->physicsParameters() - ->sublist("FluidPhysicsBlock", true) - .sublist("Data", true) - .set("Basis Order", 1); - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - auto physics_manager = Teuchos::rcp(new PhysicsManager( - std::integral_constant{}, parameter_db, mesh_manager)); - physics_manager->setupModel(); - - // Add a global parameter - physics_manager->addScalarParameter("Global Parameter", 1.234); - - // Make a parameter to change. - typename EvalType::ScalarT param_value = 2.345; - - // Make global parameter. - auto global_param = Teuchos::rcp(new Parameter::ScalarParameter( - "Global Parameter", param_value)); - EXPECT_EQ("Global Parameter", global_param->name()); - EXPECT_EQ(2.345, getValue(param_value)); - global_param->update(*(physics_manager->globalData())); - EXPECT_EQ(1.234, getValue(param_value)); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameter, residual) -{ - testScalarParameter(); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameter, jacobian) -{ - testScalarParameter(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/parameters/unit_test/tstScalarParameterEvaluator.cpp b/src/parameters/unit_test/tstScalarParameterEvaluator.cpp deleted file mode 100644 index e9cdd7f..0000000 --- a/src/parameters/unit_test/tstScalarParameterEvaluator.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "VertexCFD_ParameterUnitTestConfig.hpp" - -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "parameters/VertexCFD_ScalarParameterEvaluator.hpp" -#include "parameters/VertexCFD_ScalarParameterInput.hpp" -#include "parameters/VertexCFD_ScalarParameterManager.hpp" -#include "parameters/VertexCFD_ScalarParameterObserver.hpp" - -#include "drivers/VertexCFD_InitialConditionManager.hpp" -#include "drivers/VertexCFD_MeshManager.hpp" -#include "drivers/VertexCFD_PhysicsManager.hpp" - -#include "utils/VertexCFD_EvaluatorBase.hpp" - -#include - -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -class EvaluatorWithParameter - : public EvaluatorBase, - public Parameter::ScalarParameterObserver -{ - public: - using scalar_type = typename EvalType::ScalarT; - - EvaluatorWithParameter(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& params) - : _f1("f1", ir.dl_scalar) - , _f2("f2", ir.dl_scalar) - , _f3("f3", ir.dl_scalar) - , _f4("f4", ir.dl_scalar) - , _f5("f5", ir.dl_scalar) - { - this->addEvaluatedField(_f1); - this->addEvaluatedField(_f2); - this->addEvaluatedField(_f3); - this->addEvaluatedField(_f4); - this->addEvaluatedField(_f5); - this->registerParameter("p1", 2.0, params, _p1); - this->registerParameter("p2", 3.0, params, _p2); - this->registerParameter("p3", 4.0, params, _p3); - this->registerParameter("p4", 5.0, params, _p4); - this->registerParameter("p5", 6.0, params, _p5); - this->setName("EvaluatorWithParameter"); - } - - scalar_type _p1; - scalar_type _p2; - scalar_type _p3; - scalar_type _p4; - scalar_type _p5; - scalar_type _cp1; - scalar_type _cp2; - scalar_type _cp3; - scalar_type _cp4; - scalar_type _cp5; - PHX::MDField _f1; - PHX::MDField _f2; - PHX::MDField _f3; - PHX::MDField _f4; - PHX::MDField _f5; - - protected: - void updateStateWithNewParameters() override - { - // Do a copy here to make sure this function gets called. - _cp1 = _p1; - _cp2 = _p2; - _cp3 = _p3; - _cp4 = _p4; - _cp5 = _p5; - } - - void evaluateFieldsImpl(typename Traits::EvalData) override - { - _f1.deep_copy(_cp1); - _f2.deep_copy(_cp2); - _f3.deep_copy(_cp3); - _f4.deep_copy(_cp4); - _f5.deep_copy(_cp5); - } -}; - -//---------------------------------------------------------------------------// -template -void testScalarParameterEvaluator() -{ - // Setup base data structures to make a parameter library. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - const std::string location = VERTEXCFD_PARAMETER_TEST_DATA_DIR; - const std::string file = "scalar_parameter_evaluator_test.xml"; - auto parameter_db = Teuchos::rcp( - new Parameter::ParameterDatabase(comm, location + file)); - parameter_db->physicsParameters() - ->sublist("FluidPhysicsBlock", true) - .sublist("Data", true) - .set("Basis Order", 1); - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - auto physics_manager = Teuchos::rcp(new PhysicsManager( - std::integral_constant{}, parameter_db, mesh_manager)); - physics_manager->setupModel(); - - // Setup test fixture. - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Inject custom type for parameter input. - TEUCHOS_ADD_TYPE_CONVERTER(Parameter::ScalarParameterInput); - - // Setup parameterized evaluator. The parameter list below in XML would - // be: - // - // - // - // - // - // - // - // Note that p1 is not included to trigger the default evaluation. - Teuchos::ParameterList plist; - Parameter::ScalarParameterInput input_2 = {"Parameter2"}; - plist.set("p2", input_2); - plist.set("p3", 9.2); - Parameter::GeneralScalarParameterInput input_4 = {"GeneralParam1"}; - plist.set("p4", input_4); - Parameter::GeneralScalarParameterInput input_5 = {"GeneralParam2"}; - plist.set("p5", input_5); - auto param_eval - = Teuchos::rcp(new EvaluatorWithParameter( - *test_fixture.ir, plist)); - test_fixture.registerEvaluator(param_eval); - - // Create a parameter observer manager and register the evaluator with it. - auto param_manager = Teuchos::rcp( - new Parameter::ScalarParameterManager(*parameter_db)); - param_manager->addObserver(param_eval); - - // Setup scalar parameter evaluator. This will trigger the parameter - // update first in the graph. - auto global_data = physics_manager->globalData(); - auto sp_eval = Teuchos::rcp( - new Parameter::ScalarParameterEvaluator( - param_manager, global_data)); - test_fixture.registerEvaluator(sp_eval); - - // Add required test fields. - test_fixture.registerTestField(param_eval->_f1); - test_fixture.registerTestField(param_eval->_f2); - test_fixture.registerTestField(param_eval->_f3); - test_fixture.registerTestField(param_eval->_f4); - test_fixture.registerTestField(param_eval->_f5); - - // Set the test fixture block. - test_fixture.workset->block_id = "eblock-0_0"; - - // Evaluate test fields. - test_fixture.evaluate(); - - // Check the test fields during evaluate. - // - // Field 1 should have the default in the evaluator because it is not - // defined in the evaluator list. - // - // Field 2 should have the nominal value from the global scalar parameter - // list because it is set as a scalar parameter for the object. - // - // Field 3 should have the value from the evaluator parameter list because - // it is set as a local parameter in the evaluator list. - // - // Field 4 should have the value of the first general parameter in the - // given element block. - // - // Field 5 should have the default value of the second general - // parameter in the given element block because no block-specific values - // are given for the second parameter in the input. - auto f1_result = test_fixture.getTestFieldData(param_eval->_f1); - auto f2_result = test_fixture.getTestFieldData(param_eval->_f2); - auto f3_result = test_fixture.getTestFieldData(param_eval->_f3); - auto f4_result = test_fixture.getTestFieldData(param_eval->_f4); - auto f5_result = test_fixture.getTestFieldData(param_eval->_f5); - EXPECT_DOUBLE_EQ(2.0, fieldValue(f1_result, 0, 0)); - EXPECT_DOUBLE_EQ(5.5, fieldValue(f2_result, 0, 0)); - EXPECT_DOUBLE_EQ(9.2, fieldValue(f3_result, 0, 0)); - EXPECT_DOUBLE_EQ(2.34, fieldValue(f4_result, 0, 0)); - EXPECT_DOUBLE_EQ(3.45, fieldValue(f5_result, 0, 0)); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameter, residual) -{ - testScalarParameterEvaluator(); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameter, jacobian) -{ - testScalarParameterEvaluator(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/parameters/unit_test/tstScalarParameterInput.cpp b/src/parameters/unit_test/tstScalarParameterInput.cpp deleted file mode 100644 index 913fc49..0000000 --- a/src/parameters/unit_test/tstScalarParameterInput.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "parameters/VertexCFD_ScalarParameterInput.hpp" - -#include -#include -#include - -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -TEST(ScalarParameterInput, xml_write_read) -{ - // Inject custom type for parameter input. - TEUCHOS_ADD_TYPE_CONVERTER(Parameter::ScalarParameterInput); - - // Make a parameter list with a scalar parameter input. - Teuchos::ParameterList write_list; - Parameter::ScalarParameterInput write_input = {"Parameter Name"}; - write_list.set("Object Parameter", write_input); - - // Write a parameter list to XML. Write it to cout too so we can see it. - Teuchos::writeParameterListToXmlFile(write_list, - "scalar_parameter_input_test.xml"); - Teuchos::writeParameterListToXmlOStream(write_list, std::cout); - - // Read the parameter list back in. Also write it to cout so we can see - // it. - auto read_list - = Teuchos::getParametersFromXmlFile("scalar_parameter_input_test.xml"); - std::cout << *read_list; - - // Check result. - auto read_input - = read_list->get("Object Parameter"); - EXPECT_EQ("Parameter Name", read_input.parameter_name); - - // Check equality operator. - Parameter::ScalarParameterInput eq_check = {"Parameter Name"}; - EXPECT_EQ(eq_check, write_input); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/parameters/unit_test/tstScalarParameterObserver.cpp b/src/parameters/unit_test/tstScalarParameterObserver.cpp deleted file mode 100644 index 2670279..0000000 --- a/src/parameters/unit_test/tstScalarParameterObserver.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "VertexCFD_ParameterUnitTestConfig.hpp" - -#include "parameters/VertexCFD_GeneralScalarParameterInput.hpp" -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "parameters/VertexCFD_ScalarParameterInput.hpp" -#include "parameters/VertexCFD_ScalarParameterObserver.hpp" - -#include "drivers/VertexCFD_InitialConditionManager.hpp" -#include "drivers/VertexCFD_MeshManager.hpp" -#include "drivers/VertexCFD_PhysicsManager.hpp" - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -class TestObserver : public Parameter::ScalarParameterObserver -{ - public: - void updateStateWithNewParameters() override { _state_updated = true; } - bool _state_updated = false; -}; - -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Residual::ScalarT& observed) -{ - return observed; -} - -//---------------------------------------------------------------------------// -double getValue(const panzer::Traits::Jacobian::ScalarT& observed) -{ - return observed.val(); -} - -//---------------------------------------------------------------------------// -template -void testScalarParameterObserver() -{ - // Setup base data structures to make a parameter library. - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - const std::string location = VERTEXCFD_RESPONSE_TEST_DATA_DIR; - const std::string file = "response_manager_test.xml"; - auto parameter_db = Teuchos::rcp( - new Parameter::ParameterDatabase(comm, location + file)); - parameter_db->physicsParameters() - ->sublist("FluidPhysicsBlock", true) - .sublist("Data", true) - .set("Basis Order", 1); - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - auto physics_manager = Teuchos::rcp(new PhysicsManager( - std::integral_constant{}, parameter_db, mesh_manager)); - physics_manager->setupModel(); - - // Add a global parameter - physics_manager->addScalarParameter("Global Parameter", 1.234); - - // Make worksets to get block info. First workset has a real block, second - // does not so we can check default assignment. - panzer::Workset workset_1; - workset_1.block_id = "eblock-0_0"; - panzer::Workset workset_2; - workset_2.block_id = "undefined"; - - // Make general parameter data. - std::unordered_map> - general_parameter_data; - std::unordered_map param_values; - param_values.emplace("Default Value", 5.678); - param_values.emplace("eblock-0_0", 6.789); - general_parameter_data.emplace("General Parameter", param_values); - - // Make parameter list. - Teuchos::ParameterList plist; - - // Add a global parameter. - Parameter::ScalarParameterInput global_input = {"Global Parameter"}; - plist.set("global_param", global_input); - - // Add a general parameter. - Parameter::GeneralScalarParameterInput general_input - = {"General Parameter"}; - plist.set("general_param", general_input); - - // Add a regular parameter. - plist.set("local_param_1", 3.456); - - // Make parameters. - typename EvalType::ScalarT global_param; - typename EvalType::ScalarT general_param; - typename EvalType::ScalarT local_param_1; - typename EvalType::ScalarT local_param_2; - - // Make a parameter observer. - TestObserver observer; - observer.registerParameter("global_param", 2.345, plist, global_param); - observer.registerParameter("general_param", 0.123, plist, general_param); - observer.registerParameter("local_param_1", 4.567, plist, local_param_1); - observer.registerParameter("local_param_2", 4.567, plist, local_param_2); - - // Check pre-update values. - EXPECT_EQ(2.345, getValue(global_param)); - EXPECT_EQ(0.123, getValue(general_param)); - EXPECT_EQ(3.456, getValue(local_param_1)); - EXPECT_EQ(4.567, getValue(local_param_2)); - - // Update with the first workset. - EXPECT_FALSE(observer._state_updated); - observer.update( - *(physics_manager->globalData()), workset_1, general_parameter_data); - EXPECT_TRUE(observer._state_updated); - - // Check post-update values. - EXPECT_EQ(1.234, getValue(global_param)); - EXPECT_EQ(6.789, getValue(general_param)); - EXPECT_EQ(3.456, getValue(local_param_1)); - EXPECT_EQ(4.567, getValue(local_param_2)); - - // Update with the second workset and make sure the block default was - // given. The general param should be the only one that changes. - observer.update( - *(physics_manager->globalData()), workset_2, general_parameter_data); - EXPECT_EQ(1.234, getValue(global_param)); - EXPECT_EQ(5.678, getValue(general_param)); - EXPECT_EQ(3.456, getValue(local_param_1)); - EXPECT_EQ(4.567, getValue(local_param_2)); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameterObserver, residual) -{ - testScalarParameterObserver(); -} - -//---------------------------------------------------------------------------// -TEST(ScalarParameterObserver, jacobian) -{ - testScalarParameterObserver(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/responses/VertexCFD_ResponseManager.cpp b/src/responses/VertexCFD_ResponseManager.cpp deleted file mode 100644 index 14669ca..0000000 --- a/src/responses/VertexCFD_ResponseManager.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// Due to a conflict with FAD types and Kokkos views, this file needs to be -// included before other VertexCFD includes -#include "utils/VertexCFD_Utils_KokkosFadFixup.hpp" - -#include "VertexCFD_ResponseManager.hpp" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Response -{ -//---------------------------------------------------------------------------// -ResponseManager::ResponseManager(Teuchos::RCP physics_manager) - : _num_responses(0) - , _physics_manager(physics_manager) -{ - std::vector element_blocks; - _physics_manager->meshManager()->mesh()->getElementBlockNames( - element_blocks); - - _default_workset_descriptors.reserve(element_blocks.size()); - for (const auto& block : element_blocks) - _default_workset_descriptors.emplace_back(block); -} - -//---------------------------------------------------------------------------// -int ResponseManager::numResponses() const -{ - return _num_responses; -} - -//---------------------------------------------------------------------------// -void ResponseManager::addFunctionalResponse(const std::string& name, - const std::string& field_name) -{ - addFunctionalResponse(name, field_name, _default_workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addFunctionalResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors) -{ - // Setup the response builder. - auto builder = Teuchos::rcp( - new panzer::FunctionalResponse_Builder); - builder->comm - = Teuchos::getRawMpiComm(*_physics_manager->meshManager()->comm()); - builder->cubatureDegree = _physics_manager->integrationOrder(); - builder->requiresCellIntegral = true; - builder->quadPointField = field_name; - builder->applyDirichletToDerivative = false; - - addResponseFromBuilder(name, workset_descriptors, builder); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addMinValueResponse(const std::string& name, - const std::string& field_name) -{ - addMinValueResponse(name, field_name, _default_workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addMinValueResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors) -{ - constexpr int use_max = false; - addExtremeValueResponse(use_max, name, field_name, workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addMaxValueResponse(const std::string& name, - const std::string& field_name) -{ - addMaxValueResponse(name, field_name, _default_workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addMaxValueResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors) -{ - constexpr int use_max = true; - addExtremeValueResponse(use_max, name, field_name, workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addExtremeValueResponse( - const bool use_max, - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors) -{ - // Setup the response builder. - auto builder = Teuchos::rcp( - new panzer::ExtremeValueResponse_Builder); - builder->comm - = Teuchos::getRawMpiComm(*_physics_manager->meshManager()->comm()); - builder->cubatureDegree = _physics_manager->integrationOrder(); - builder->requiresCellExtreme = true; - builder->useMax = use_max; - builder->quadPointField = field_name; - builder->applyDirichletToDerivative = false; - builder->prefix = use_max ? "Max " : "Min "; - - addResponseFromBuilder(name, workset_descriptors, builder); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addProbeResponse(const std::string& name, - const std::string& field_name, - const Teuchos::Array& point) -{ - addProbeResponse(name, field_name, point, _default_workset_descriptors); -} - -//---------------------------------------------------------------------------// -void ResponseManager::addProbeResponse( - const std::string& name, - const std::string& field_name, - const Teuchos::Array& point, - const std::vector& workset_descriptors) -{ - // Setup the response builder. - auto builder = Teuchos::rcp( - new panzer::ProbeResponse_Builder); - builder->comm - = Teuchos::getRawMpiComm(*_physics_manager->meshManager()->comm()); - builder->cubatureDegree = _physics_manager->integrationOrder(); - builder->fieldName = field_name; - builder->fieldComponent = 0; - builder->point = point; - builder->applyDirichletToDerivative = false; - - addResponseFromBuilder(name, workset_descriptors, builder); -} - -//---------------------------------------------------------------------------// -template -void ResponseManager::addResponseFromBuilder( - const std::string& name, - const std::vector& workset_descriptors, - const Builder& builder) -{ - auto model_evaluator = _physics_manager->modelEvaluator(); - - // If no workset descriptors were provided, use the default - // (all element blocks). - const auto& workset_desc = workset_descriptors.empty() - ? _default_workset_descriptors - : workset_descriptors; - - const int response_index - = model_evaluator->addFlexibleResponse(name, workset_desc, builder); - - _resp_vectors.emplace_back( - Thyra::createMember(model_evaluator->get_g_space(response_index))); - - _index_map.emplace_back(response_index); - _name_map.emplace(name, _num_responses); - _is_active.emplace_back(true); - - // Add a scalar parameter to store the result of the response. Default - // value is zero. - panzer::registerScalarParameter( - name, *(_physics_manager->globalData()->pl), 0.0); - - ++_num_responses; -} - -//---------------------------------------------------------------------------// -void ResponseManager::activateResponse(const int index) -{ - _is_active.at(index) = true; -} - -//---------------------------------------------------------------------------// -void ResponseManager::activateResponse(const std::string& name) -{ - const int index = _name_map.at(name); - activateResponse(index); -} - -//---------------------------------------------------------------------------// -void ResponseManager::deactivateAll() -{ - std::fill(_is_active.begin(), _is_active.end(), false); -} - -//---------------------------------------------------------------------------// -void ResponseManager::evaluateResponses( - const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot) -{ - const int num_active - = std::count(_is_active.begin(), _is_active.end(), true); - - // Just return if there are no active responses. - if (num_active == 0) - return; - - auto model_evaluator = _physics_manager->modelEvaluator(); - - auto in_args = model_evaluator->createInArgs(); - auto out_args = model_evaluator->createOutArgs(); - - in_args.set_x(x); - in_args.set_x_dot(x_dot); - - // Set output vector for each active response. - for (int i = 0; i < _num_responses; ++i) - { - if (_is_active[i]) - { - out_args.set_g(_index_map[i], _resp_vectors[i]); - } - } - - // Evaluate the response. - model_evaluator->evalModel(in_args, out_args); - - // Extract the value and insert it into the parameter library. - for (int i = 0; i < _num_responses; ++i) - { - panzer::registerScalarParameter( - name(i), *(_physics_manager->globalData()->pl), value(i)); - } -} - -//---------------------------------------------------------------------------// -int ResponseManager::globalIndex(const int index) const -{ - return _index_map.at(index); -} - -//---------------------------------------------------------------------------// -int ResponseManager::globalIndex(const std::string& name) const -{ - const int index = _name_map.at(name); - return globalIndex(index); -} - -//---------------------------------------------------------------------------// -const std::string& ResponseManager::name(const int index) const -{ - return _physics_manager->modelEvaluator()->get_g_name(globalIndex(index)); -} - -//---------------------------------------------------------------------------// -double ResponseManager::value(const int index) const -{ - return Thyra::get_ele(*_resp_vectors.at(index), 0); -} - -//---------------------------------------------------------------------------// -double ResponseManager::value(const std::string& name) const -{ - const int index = _name_map.at(name); - return value(index); -} - -//---------------------------------------------------------------------------// - -} // namespace Response -} // namespace VertexCFD diff --git a/src/responses/VertexCFD_ResponseManager.hpp b/src/responses/VertexCFD_ResponseManager.hpp deleted file mode 100644 index 81ca66d..0000000 --- a/src/responses/VertexCFD_ResponseManager.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef VERTEXCFD_RESPONSEMANAGER_HPP -#define VERTEXCFD_RESPONSEMANAGER_HPP - -#include "drivers/VertexCFD_PhysicsManager.hpp" - -#include - -#include - -#include -#include - -#include -#include -#include - -namespace VertexCFD -{ -namespace Response -{ -class ResponseManager -{ - public: - explicit ResponseManager(Teuchos::RCP physics_manager); - - void addFunctionalResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors); - void addFunctionalResponse(const std::string& name, - const std::string& field_name); - void addMinValueResponse(const std::string& name, - const std::string& field_name); - void addMinValueResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors); - void addMaxValueResponse( - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors); - void addMaxValueResponse(const std::string& name, - const std::string& field_name); - void addProbeResponse( - const std::string& name, - const std::string& field_name, - const Teuchos::Array& point, - const std::vector& workset_descriptors); - void addProbeResponse(const std::string& name, - const std::string& field_name, - const Teuchos::Array& point); - void activateResponse(const int index = 0); - void activateResponse(const std::string& name); - void deactivateAll(); - void - evaluateResponses(const Teuchos::RCP>& x, - const Teuchos::RCP>& x_dot); - - int numResponses() const; - int globalIndex(const int index = 0) const; - int globalIndex(const std::string& name) const; - const std::string& name(const int index = 0) const; - double value(const int index = 0) const; - double value(const std::string& name) const; - - private: - int _num_responses; - Teuchos::RCP _physics_manager; - std::vector _default_workset_descriptors; - std::vector _index_map; - std::unordered_map _name_map; - std::vector>> _resp_vectors; - std::vector _is_active; - - void addExtremeValueResponse( - const bool use_max, - const std::string& name, - const std::string& field_name, - const std::vector& workset_descriptors); - - template - void addResponseFromBuilder( - const std::string& name, - const std::vector& workset_descriptors, - const Builder& builder); -}; - -} // namespace Response -} // namespace VertexCFD - -#endif // VERTEXCFD_RESPONSEMANAGER_HPP diff --git a/src/responses/VertexCFD_Response_Utils.cpp b/src/responses/VertexCFD_Response_Utils.cpp deleted file mode 100644 index 897846e..0000000 --- a/src/responses/VertexCFD_Response_Utils.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "VertexCFD_Response_Utils.hpp" - -#include - -namespace VertexCFD -{ -namespace Response -{ - -//---------------------------------------------------------------------------// -std::vector -buildWorksetDescriptors(const Teuchos::ParameterList& plist) -{ - std::vector workset_descriptors; - - // Let user set specific element blocks for volume integrals. - if (plist.isType("Element Blocks")) - { - std::vector element_blocks; - panzer::StringTokenizer( - element_blocks, plist.get("Element Blocks"), ",", true); - - workset_descriptors.reserve(element_blocks.size()); - for (const auto& block : element_blocks) - workset_descriptors.emplace_back(block); - } - // Let user set specific sidesets for surface integrals. - else if (plist.isSublist("Sidesets")) - { - const auto& sideset_plist = plist.sublist("Sidesets"); - std::vector sidesets; - - for (auto sideset_itr = sideset_plist.begin(); - sideset_itr != sideset_plist.end(); - ++sideset_itr) - { - const auto& block = sideset_itr->first; - - sidesets.clear(); - panzer::StringTokenizer( - sidesets, sideset_plist.get(block), ",", true); - - for (const auto& side : sidesets) - workset_descriptors.emplace_back(block, side); - } - } - - return workset_descriptors; -} - -} // namespace Response -} // namespace VertexCFD diff --git a/src/responses/VertexCFD_Response_Utils.hpp b/src/responses/VertexCFD_Response_Utils.hpp deleted file mode 100644 index 0136b47..0000000 --- a/src/responses/VertexCFD_Response_Utils.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef VERTEXCFD_RESPONSE_UTILS_HPP -#define VERTEXCFD_RESPONSE_UTILS_HPP - -// Need to make TEUCHOS_TEST_FOR_EXCEPTION available to Panzer. -#include - -// This uses TEUCHOS_TEST_FOR_EXCEPTION, but doesn't inlude the required -// header. -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Response -{ - -std::vector -buildWorksetDescriptors(const Teuchos::ParameterList& sideset_plist); - -} // namespace Response -} // namespace VertexCFD - -#endif // VERTEXCFD_RESPONSE_UTILS_HPP diff --git a/src/responses/unit_test/CMakeLists.txt b/src/responses/unit_test/CMakeLists.txt deleted file mode 100644 index 84c6b9e..0000000 --- a/src/responses/unit_test/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -configure_file( VertexCFD_ResponseUnitTestConfig.hpp.cmakein VertexCFD_ResponseUnitTestConfig.hpp ) - -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - ResponseManager - ResponseUtils - ) - -# The ResponseManager test relies on Panzer capability that uses CUDA UVM. -# This must be launched with CUDA_LAUNCH_BLOCKING to work correctly. -if(${VERTEXCFD_KOKKOS_DEVICE_TYPE} STREQUAL "CUDA") - message("Setting ResponseManager launch env") - set_property(TEST VertexCFD_ResponseManager_test_CUDA PROPERTY ENVIRONMENT "CUDA_LAUNCH_BLOCKING=1") -endif() diff --git a/src/responses/unit_test/VertexCFD_ResponseUnitTestConfig.hpp.cmakein b/src/responses/unit_test/VertexCFD_ResponseUnitTestConfig.hpp.cmakein deleted file mode 100644 index e924f9a..0000000 --- a/src/responses/unit_test/VertexCFD_ResponseUnitTestConfig.hpp.cmakein +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef VERTEXCFD_RESPONSEUNITTESTCONFIG_HPP -#define VERTEXCFD_RESPONSEUNITTESTCONFIG_HPP - -constexpr char VERTEXCFD_RESPONSE_TEST_DATA_DIR[] = R"(@CMAKE_SOURCE_DIR@/src/responses/unit_test/data/)"; -constexpr char VERTEXCFD_RESPONSE_TEST_INPUT_DIR[] = R"(@CMAKE_SOURCE_DIR@/examples/inputs/)"; -constexpr char VERTEXCFD_RESPONSE_TEST_MESH_DIR[] = R"(@CMAKE_SOURCE_DIR@/examples/mesh/)"; - -#endif // end VERTEXCFD_RESPONSEUNITTESTCONFIG_HPP diff --git a/src/responses/unit_test/data/response_manager_test.xml b/src/responses/unit_test/data/response_manager_test.xml deleted file mode 100644 index 55e9e68..0000000 --- a/src/responses/unit_test/data/response_manager_test.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/responses/unit_test/tstResponseManager.cpp b/src/responses/unit_test/tstResponseManager.cpp deleted file mode 100644 index 9ca3473..0000000 --- a/src/responses/unit_test/tstResponseManager.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "VertexCFD_ResponseUnitTestConfig.hpp" - -#include "drivers/VertexCFD_InitialConditionManager.hpp" -#include "drivers/VertexCFD_MeshManager.hpp" -#include "drivers/VertexCFD_PhysicsManager.hpp" -#include "parameters/VertexCFD_ParameterDatabase.hpp" -#include "responses/VertexCFD_ResponseManager.hpp" - -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -void testScalarParameters(const bool before, - const Teuchos::RCP& physics_manager) -{ - using scalar_type = typename EvalType::ScalarT; - auto pl = physics_manager->globalData()->pl; - - auto get_scalar_value = [=](const std::string& name) { - return Sacado::ScalarValue::eval( - panzer::accessScalarParameter(name, *pl)->getValue()); - }; - - if (before) - { - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("u integral")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("v integral")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("u min")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("u max")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("v min")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("v max")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("u probe")); - EXPECT_DOUBLE_EQ(0.0, get_scalar_value("v probe")); - } - - else - { - EXPECT_DOUBLE_EQ(4.0, get_scalar_value("u integral")); - EXPECT_DOUBLE_EQ(6.0, get_scalar_value("v integral")); - EXPECT_DOUBLE_EQ(2.0, get_scalar_value("u min")); - EXPECT_DOUBLE_EQ(2.0, get_scalar_value("u max")); - EXPECT_DOUBLE_EQ(3.0, get_scalar_value("v min")); - EXPECT_DOUBLE_EQ(3.0, get_scalar_value("v max")); - EXPECT_DOUBLE_EQ(2.0, get_scalar_value("u probe")); - EXPECT_DOUBLE_EQ(3.0, get_scalar_value("v probe")); - } -} - -//---------------------------------------------------------------------------// -void testResponseManager(const int basis_order) -{ - auto comm = Teuchos::rcp_dynamic_cast>( - Teuchos::DefaultComm::getComm()); - - const std::string location = VERTEXCFD_RESPONSE_TEST_DATA_DIR; - const std::string file = "response_manager_test.xml"; - - auto parameter_db = Teuchos::rcp( - new Parameter::ParameterDatabase(comm, location + file)); - - parameter_db->physicsParameters() - ->sublist("FluidPhysicsBlock", true) - .sublist("Data", true) - .set("Basis Order", basis_order); - - auto mesh_manager = Teuchos::rcp(new MeshManager(*parameter_db, comm)); - - auto physics_manager = Teuchos::rcp(new PhysicsManager( - std::integral_constant{}, parameter_db, mesh_manager)); - - Response::ResponseManager response_manager(physics_manager); - - // Volume responses over all element blocks. - response_manager.addFunctionalResponse("u integral", "velocity_0"); - response_manager.addFunctionalResponse("v integral", "velocity_1"); - response_manager.addMinValueResponse("u min", "velocity_0"); - response_manager.addMaxValueResponse("u max", "velocity_0"); - response_manager.addMinValueResponse("v min", "velocity_1"); - response_manager.addMaxValueResponse("v max", "velocity_1"); - Teuchos::Array point(2); - point[0] = 0.21875; - point[1] = 1.5625; - response_manager.addProbeResponse("u probe", "velocity_0", point); - response_manager.addProbeResponse("v probe", "velocity_1", point); - - // Surface responses over all sidesets. - std::vector sideset_descriptors = { - {"eblock-0_0", "top"}, - {"eblock-0_0", "bottom"}, - {"eblock-0_0", "left"}, - {"eblock-0_0", "right"}, - }; - response_manager.addFunctionalResponse( - "u surface integral", "velocity_0", sideset_descriptors); - response_manager.addFunctionalResponse( - "v surface integral", "velocity_1", sideset_descriptors); - - // Finish physics. - physics_manager->setupModel(); - - InitialConditionManager ic_manager(parameter_db, mesh_manager); - - Teuchos::RCP> x; - Teuchos::RCP> x_dot; - ic_manager.applyInitialConditions( - std::integral_constant{}, *physics_manager, x, x_dot); - - // We will test responses of velocities, which are linear in (x,y): - // u = -0.25 * y - // v = 0.25 * x - - EXPECT_EQ(10, response_manager.numResponses()); - - for (int i = 0; i < 10; ++i) - EXPECT_EQ(i, response_manager.globalIndex(i)); - - EXPECT_EQ(0, response_manager.globalIndex("u integral")); - EXPECT_EQ(1, response_manager.globalIndex("v integral")); - EXPECT_EQ(2, response_manager.globalIndex("u min")); - EXPECT_EQ(3, response_manager.globalIndex("u max")); - EXPECT_EQ(4, response_manager.globalIndex("v min")); - EXPECT_EQ(5, response_manager.globalIndex("v max")); - EXPECT_EQ(6, response_manager.globalIndex("u probe")); - EXPECT_EQ(7, response_manager.globalIndex("v probe")); - EXPECT_EQ(8, response_manager.globalIndex("u surface integral")); - EXPECT_EQ(9, response_manager.globalIndex("v surface integral")); - - EXPECT_EQ("u integral", response_manager.name(0)); - EXPECT_EQ("v integral", response_manager.name(1)); - EXPECT_EQ("u min", response_manager.name(2)); - EXPECT_EQ("u max", response_manager.name(3)); - EXPECT_EQ("v min", response_manager.name(4)); - EXPECT_EQ("v max", response_manager.name(5)); - EXPECT_EQ("u probe", response_manager.name(6)); - EXPECT_EQ("v probe", response_manager.name(7)); - EXPECT_EQ("u surface integral", response_manager.name(8)); - EXPECT_EQ("v surface integral", response_manager.name(9)); - - testScalarParameters(true, physics_manager); - testScalarParameters(true, physics_manager); - testScalarParameters(true, physics_manager); - - response_manager.evaluateResponses(x, x_dot); - - EXPECT_DOUBLE_EQ(4.0, response_manager.value(0)); - EXPECT_DOUBLE_EQ(6.0, response_manager.value(1)); - EXPECT_DOUBLE_EQ(2.0, response_manager.value(2)); - EXPECT_DOUBLE_EQ(2.0, response_manager.value(3)); - EXPECT_DOUBLE_EQ(3.0, response_manager.value(4)); - EXPECT_DOUBLE_EQ(3.0, response_manager.value(5)); - EXPECT_DOUBLE_EQ(2.0, response_manager.value(6)); - EXPECT_DOUBLE_EQ(3.0, response_manager.value(7)); - EXPECT_DOUBLE_EQ(12.0, response_manager.value(8)); - EXPECT_DOUBLE_EQ(18.0, response_manager.value(9)); - - EXPECT_DOUBLE_EQ(4.0, response_manager.value("u integral")); - EXPECT_DOUBLE_EQ(6.0, response_manager.value("v integral")); - EXPECT_DOUBLE_EQ(2.0, response_manager.value("u min")); - EXPECT_DOUBLE_EQ(2.0, response_manager.value("u max")); - EXPECT_DOUBLE_EQ(3.0, response_manager.value("v min")); - EXPECT_DOUBLE_EQ(3.0, response_manager.value("v max")); - EXPECT_DOUBLE_EQ(2.0, response_manager.value("u probe")); - EXPECT_DOUBLE_EQ(3.0, response_manager.value("v probe")); - EXPECT_DOUBLE_EQ(12.0, response_manager.value("u surface integral")); - EXPECT_DOUBLE_EQ(18.0, response_manager.value("v surface integral")); - - testScalarParameters(false, physics_manager); - testScalarParameters(false, physics_manager); - testScalarParameters(false, physics_manager); -} - -//---------------------------------------------------------------------------// -TEST(ResponseManager, BasisOrder1) -{ - testResponseManager(1); -} - -//---------------------------------------------------------------------------// -TEST(ResponseManager, BasisOrder2) -{ - testResponseManager(2); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/responses/unit_test/tstResponseUtils.cpp b/src/responses/unit_test/tstResponseUtils.cpp deleted file mode 100644 index 613ee04..0000000 --- a/src/responses/unit_test/tstResponseUtils.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "responses/VertexCFD_Response_Utils.hpp" - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ - -//---------------------------------------------------------------------------// -TEST(BuildWorksetDescriptors, FromElementBlocks) -{ - const auto params = Teuchos::ParameterList("Volume Response") - .set("Element Blocks", "block1,block2"); - auto workset_descriptors = Response::buildWorksetDescriptors(params); - - ASSERT_EQ(2, workset_descriptors.size()); - - EXPECT_EQ("block1", workset_descriptors[0].getElementBlock()); - EXPECT_FALSE(workset_descriptors[0].useSideset()); - - EXPECT_EQ("block2", workset_descriptors[1].getElementBlock()); - EXPECT_FALSE(workset_descriptors[1].useSideset()); -} - -//---------------------------------------------------------------------------// -TEST(BuildWorksetDescriptors, FromSidesets) -{ - const auto params = Teuchos::ParameterList("Surface Response") - .set("Sidesets", - Teuchos::ParameterList() - .set("block1", "side1,side2") - .set("block2", "side2")); - auto workset_descriptors = Response::buildWorksetDescriptors(params); - - ASSERT_EQ(3, workset_descriptors.size()); - - EXPECT_EQ("block1", workset_descriptors[0].getElementBlock()); - EXPECT_TRUE(workset_descriptors[0].useSideset()); - EXPECT_EQ("side1", workset_descriptors[0].getSideset()); - - EXPECT_EQ("block1", workset_descriptors[1].getElementBlock()); - EXPECT_TRUE(workset_descriptors[1].useSideset()); - EXPECT_EQ("side2", workset_descriptors[1].getSideset()); - - EXPECT_EQ("block2", workset_descriptors[2].getElementBlock()); - EXPECT_TRUE(workset_descriptors[2].useSideset()); - EXPECT_EQ("side2", workset_descriptors[2].getSideset()); -} - -//---------------------------------------------------------------------------// - -} // namespace Test -} // namespace VertexCFD diff --git a/src/test_harness/CMakeLists.txt b/src/test_harness/CMakeLists.txt deleted file mode 100644 index 3263f4b..0000000 --- a/src/test_harness/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -set(gtest_args --gtest_color=yes PARENT_SCOPE) -add_subdirectory(unit_test) diff --git a/src/test_harness/TestHarness.cmake b/src/test_harness/TestHarness.cmake deleted file mode 100644 index b2054fd..0000000 --- a/src/test_harness/TestHarness.cmake +++ /dev/null @@ -1,80 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) - -##--------------------------------------------------------------------------## -## Test Macro -##--------------------------------------------------------------------------## -macro(VertexCFD_add_tests) - set(options OPTIONAL MPI) - set(oneValueArgs) - set(multiValueArgs LIBS NAMES) - cmake_parse_arguments(VERTEXCFD_UNIT_TEST "${options}" "${oneValueArgs}" - "${multiValueArgs}" ${ARGN} ) - set(VERTEXCFD_UNIT_TEST_MPIEXEC_NUMPROCS 1) - foreach( _procs 2 4 ) - if(MPIEXEC_MAX_NUMPROCS GREATER_EQUAL ${_procs}) - list(APPEND VERTEXCFD_UNIT_TEST_MPIEXEC_NUMPROCS ${_procs}) - endif() - endforeach() - set(VERTEXCFD_UNIT_TEST_NUMTHREADS 1) - foreach( _threads 2 4 ) - if(MPIEXEC_MAX_NUMPROCS GREATER_EQUAL ${_threads}) - list(APPEND VERTEXCFD_UNIT_TEST_NUMTHREADS ${_threads}) - endif() - endforeach() - if(MPIEXEC_MAX_NUMPROCS GREATER 4) - list(APPEND VERTEXCFD_UNIT_TEST_MPIEXEC_NUMPROCS ${MPIEXEC_MAX_NUMPROCS}) - list(APPEND VERTEXCFD_UNIT_TEST_NUMTHREADS ${MPIEXEC_MAX_NUMPROCS}) - endif() - set(VERTEXCFD_UNIT_TEST_MAIN ${TEST_HARNESS_DIR}/test_main.cpp) - set(_device ${VERTEXCFD_KOKKOS_DEVICE_TYPE}) - set(_dir ${CMAKE_CURRENT_SOURCE_DIR}) - foreach(_test ${VERTEXCFD_UNIT_TEST_NAMES}) - set(_file ${_dir}/tst${_test}.cpp) - set(_target VertexCFD_${_test}_test_${_device}) - add_executable(${_target} ${_file} ${VERTEXCFD_UNIT_TEST_MAIN}) - target_include_directories(${_target} PRIVATE ${_dir} - ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_HARNESS_DIR}) - target_link_libraries(${_target} PRIVATE ${VERTEXCFD_UNIT_TEST_LIBS} GTest::gtest Kokkos::kokkos MPI::MPI_CXX ) - if(VERTEXCFD_UNIT_TEST_MPI) - foreach(_procs ${VERTEXCFD_UNIT_TEST_MPIEXEC_NUMPROCS}) - # NOTE: When moving to CMake 3.10+ make sure to use MPIEXEC_EXECUTABLE instead - if(_device STREQUAL PTHREAD OR _device STREQUAL OPENMP) - foreach(_threads ${VERTEXCFD_UNIT_TEST_NUMTHREADS}) - math(EXPR _total_threads "${_procs} * ${_threads}") - if(_total_threads GREATER MPIEXEC_MAX_NUMPROCS) - break() - endif() - set(_test_name ${_target}_np_${_procs}_nt_${_threads}) - add_test(NAME ${_test_name} COMMAND - ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${_procs} ${MPIEXEC_PREFLAGS} - ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args} --kokkos-threads=${_threads}) - set_property(TEST ${_test_name} PROPERTY PROCESSORS ${_total_threads}) - set_property(TEST ${_test_name} PROPERTY ENVIRONMENT "OMP_NUM_THREADS=${_threads}") - endforeach() - else() - set(_test_name ${_target}_np_${_procs}) - add_test(NAME ${_target}_np_${_procs} COMMAND - ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${_procs} ${MPIEXEC_PREFLAGS} - ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args} --kokkos-threads=1) - set_property(TEST ${_test_name} PROPERTY PROCESSORS ${_procs}) - endif() - endforeach() - else() - if(_device STREQUAL OPENMP) - foreach(_threads ${VERTEXCFD_UNIT_TEST_NUMTHREADS}) - set(_test_name ${_target}_nt_${_threads}) - add_test(NAME ${_target}_nt_${_threads} COMMAND - ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 1 ${MPIEXEC_PREFLAGS} - ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args} --kokkos-threads=${_threads}) - set_property(TEST ${_test_name} PROPERTY PROCESSORS ${_threads}) - set_property(TEST ${_test_name} PROPERTY ENVIRONMENT "OMP_NUM_THREADS=${_threads}") - endforeach() - else() - add_test(NAME ${_target} COMMAND - ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 1 ${MPIEXEC_PREFLAGS} - ${_target} ${MPIEXEC_POSTFLAGS} ${gtest_args} --kokkos-threads=1) - set_property(TEST ${_target} PROPERTY PROCESSORS 1) - endif() - endif() - endforeach() -endmacro() diff --git a/src/test_harness/VertexCFD_EvaluatorTestHarness.hpp b/src/test_harness/VertexCFD_EvaluatorTestHarness.hpp deleted file mode 100644 index 08ca391..0000000 --- a/src/test_harness/VertexCFD_EvaluatorTestHarness.hpp +++ /dev/null @@ -1,367 +0,0 @@ -#ifndef VERTEXCFD_EVALUATORTESTHARNESS_HPP -#define VERTEXCFD_EVALUATORTESTHARNESS_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include -#include -#include -#include - -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Evaluator cell test fixture. -struct EvaluatorTestFixture -{ - Teuchos::RCP> fm; - Teuchos::RCP cell_topo; - Teuchos::RCP workset; - Teuchos::RCP cell_data; - Teuchos::RCP ir; - Teuchos::RCP> int_values; - Teuchos::RCP basis_ir_layout; - Teuchos::RCP> basis_values; - - using host_coords_view = Kokkos::View::type, - PHX::Device>::HostMirror; - - // Create an evaluator test fixture object. If a side id is not specified - // then integration rules will be setup over the volume of the cell - // instead of the side. - EvaluatorTestFixture(const CellTopologyData* cell_topo_data, - const host_coords_view host_coords, - const int integration_order, - const int basis_order, - const int side_id = -1) - { - initialize(cell_topo_data, - host_coords, - integration_order, - basis_order, - side_id); - } - - // Use a default cell topology. - EvaluatorTestFixture(const int num_space_dim, - const int integration_order, - const int basis_order, - const int side_id = -1) - { - // Single cell. - const int num_cell = 1; - - const CellTopologyData* cell_topo_data = nullptr; - host_coords_view host_coords; - - // Build a line, quad, or hex as the test cell. - if (1 == num_space_dim) - { - cell_topo_data = shards::getCellTopologyData>(); - - host_coords = host_coords_view("coords", num_cell, 2, 1); - - host_coords(0, 0, 0) = 0.0; - host_coords(0, 1, 0) = 1.0; - } - else if (2 == num_space_dim) - { - cell_topo_data - = shards::getCellTopologyData>(); - - host_coords = host_coords_view("coords", num_cell, 4, 2); - - host_coords(0, 0, 0) = 0.0; - host_coords(0, 0, 1) = 0.0; - - host_coords(0, 1, 0) = 1.0; - host_coords(0, 1, 1) = 0.0; - - host_coords(0, 2, 0) = 1.0; - host_coords(0, 2, 1) = 1.0; - - host_coords(0, 3, 0) = 0.0; - host_coords(0, 3, 1) = 1.0; - } - else if (3 == num_space_dim) - { - cell_topo_data - = shards::getCellTopologyData>(); - - host_coords = host_coords_view("coords", num_cell, 8, 3); - - host_coords(0, 0, 0) = 0.0; - host_coords(0, 0, 1) = 0.0; - host_coords(0, 0, 2) = 0.0; - - host_coords(0, 1, 0) = 1.0; - host_coords(0, 1, 1) = 0.0; - host_coords(0, 1, 2) = 0.0; - - host_coords(0, 2, 0) = 1.0; - host_coords(0, 2, 1) = 1.0; - host_coords(0, 2, 2) = 0.0; - - host_coords(0, 3, 0) = 0.0; - host_coords(0, 3, 1) = 1.0; - host_coords(0, 3, 2) = 0.0; - - host_coords(0, 4, 0) = 0.0; - host_coords(0, 4, 1) = 0.0; - host_coords(0, 4, 2) = 1.0; - - host_coords(0, 5, 0) = 1.0; - host_coords(0, 5, 1) = 0.0; - host_coords(0, 5, 2) = 1.0; - - host_coords(0, 6, 0) = 1.0; - host_coords(0, 6, 1) = 1.0; - host_coords(0, 6, 2) = 1.0; - - host_coords(0, 7, 0) = 0.0; - host_coords(0, 7, 1) = 1.0; - host_coords(0, 7, 2) = 1.0; - } - else - { - std::ostringstream msg; - msg << "Invalid spatial dimensions (" << num_space_dim - << "): must be 1, 2, or 3."; - throw std::logic_error(msg.str()); - } - - initialize(cell_topo_data, - host_coords, - integration_order, - basis_order, - side_id); - } - - // Add an evaluator to the manager. - template - void - registerEvaluator(const Teuchos::RCP>& eval) - { - fm->registerEvaluator(eval); - } - - // Register fields that will be checked in the test. - template - void registerTestField(const Field& field) - { - fm->requireField(field.fieldTag()); - } - - // Set time. - void setTime(const double& time) { workset->time = time; } - - // Set time step size. - void setStepSize(const double& step_size) - { - workset->step_size = step_size; - } - - // Evaluate. - template - void evaluate() - { - panzer::Traits::SD setup_data; - auto worksets = Teuchos::rcp(new std::vector); - worksets->push_back(*workset); - setup_data.worksets_ = worksets; - std::vector derivative_dimensions; - derivative_dimensions.push_back(4); - fm->setKokkosExtendedDataTypeDimensions( - derivative_dimensions); - fm->postRegistrationSetup(setup_data); - panzer::Traits::PED ped; - fm->preEvaluate(ped); - fm->evaluateFields(*workset); - fm->postEvaluate(0); - } - - // Get a test field on the host to test after evaluation. - template - auto getTestFieldData(const Field& field) const - { - auto field_view = field.get_static_view(); - auto field_mirror = Kokkos::create_mirror(field_view); - Kokkos::deep_copy(field_mirror, field_view); - return field_mirror; - } - - // Get the number of quadrature points. - int numPoint() const { return ir->num_points; } - - // Get the number of basis points. - int cardinality() const { return basis_ir_layout->cardinality(); } - - private: - void initialize(const CellTopologyData* cell_topo_data, - const host_coords_view host_coords, - const int integration_order, - const int basis_order, - const int side_id) - { - cell_topo = Teuchos::rcp(new shards::CellTopology(cell_topo_data)); - - const int num_space_dim = cell_topo->getDimension(); - - if (num_space_dim != host_coords.extent_int(2)) - { - std::ostringstream msg; - msg << "Unexpected spatial dimensions in provided coordinates: " - << cell_topo->getName() << " expects " << num_space_dim - << " dimensions, but " << host_coords.extent(2) - << " were provided."; - throw std::logic_error(msg.str()); - } - - const int nodes_per_cell = cell_topo->getNodeCount(); - - if (nodes_per_cell != host_coords.extent_int(1)) - { - std::ostringstream msg; - msg << "Unexpected node count in provided coordinates: " - << cell_topo->getName() << " expects " << nodes_per_cell - << " nodes, but " << host_coords.extent(1) - << " were provided."; - throw std::logic_error(msg.str()); - } - - const int num_cell = host_coords.extent(0); - - // Create field manager. - fm = Teuchos::rcp(new PHX::FieldManager); - - // Create the workset. - workset = Teuchos::rcp(new panzer::Workset); - workset->num_cells = num_cell; - - // Fill cell local IDs - { - Kokkos::View cell_local_ids_k( - "cell_local_ids_k", num_cell); - auto cell_local_ids_k_host - = Kokkos::create_mirror_view(cell_local_ids_k); - - workset->cell_local_ids.resize(num_cell); - - for (int i = 0; i < num_cell; ++i) - { - cell_local_ids_k_host(i) = i; - workset->cell_local_ids[i] = i; - } - - Kokkos::deep_copy(cell_local_ids_k, cell_local_ids_k_host); - workset->cell_local_ids_k = cell_local_ids_k; - } - - workset->block_id = "block_0"; - workset->ir_degrees = Teuchos::rcp(new std::vector); - workset->basis_names = Teuchos::rcp(new std::vector); - workset->time = 0.0; - workset->step_size = 0.0; - - panzer::MDFieldArrayFactory array_factory("", true); -#if TRILINOS_MAJOR_MINOR_VERSION >= 140500 - auto& node_coords = workset->cell_node_coordinates; -#else - auto& node_coords = workset->cell_vertex_coordinates; -#endif - - node_coords - = array_factory - .buildStaticArray( - "coords", num_cell, nodes_per_cell, num_space_dim); - - Kokkos::deep_copy(node_coords.get_static_view(), host_coords); - - // Setup a cell. - cell_data - = Teuchos::rcp(new panzer::CellData(num_cell, side_id, cell_topo)); - - // Create integration rule and populate integration values. - ir = Teuchos::rcp( - new panzer::IntegrationRule(integration_order, *cell_data)); - int_values - = Teuchos::rcp(new panzer::IntegrationValues2("", true)); - int_values->setupArrays(ir); - int_values->evaluateValues(node_coords); - workset->ir_degrees->push_back(ir->cubature_degree); - workset->int_rules.push_back(int_values); - - // Create basis and populate basis values. - basis_ir_layout = panzer::basisIRLayout("HGrad", basis_order, *ir); - basis_values - = Teuchos::rcp(new panzer::BasisValues2("", true, true)); - basis_values->setupArrays(basis_ir_layout); - basis_values->evaluateValues(int_values->cub_points, - int_values->jac, - int_values->jac_det, - int_values->jac_inv, - int_values->weighted_measure, - int_values->node_coordinates); - workset->bases.push_back(basis_values); - workset->basis_names->push_back(basis_ir_layout->getBasis()->name()); - } -}; - -//---------------------------------------------------------------------------// -// For a residual evaluation, the field value is a double and returned as-is. -double accessValue(double v) -{ - return v; -} - -//---------------------------------------------------------------------------// -// For a Jacobian evaluation, the field value is Sacado FAD type, so return its -// value. -template -double accessValue(Value&& v) -{ - return accessValue(v.val()); -} - -//---------------------------------------------------------------------------// -// Access a field value at the given indices. -template -double fieldValue(Field f, const Indices... i) -{ - return accessValue(f(i...)); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EVALUATORTESTHARNESS_HPP diff --git a/src/test_harness/test_main.cpp b/src/test_harness/test_main.cpp deleted file mode 100644 index a633a63..0000000 --- a/src/test_harness/test_main.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -#include - -#include - -int main(int argc, char* argv[]) -{ - MPI_Init(&argc, &argv); - Kokkos::initialize(argc, argv); - ::testing::InitGoogleTest(&argc, argv); - int return_val = RUN_ALL_TESTS(); - Kokkos::finalize(); - MPI_Finalize(); - return return_val; -} diff --git a/src/test_harness/unit_test/CMakeLists.txt b/src/test_harness/unit_test/CMakeLists.txt deleted file mode 100644 index 30ec326..0000000 --- a/src/test_harness/unit_test/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -include_directories(${Trilinos_INCLUDE_DIRS} ${Trilinos_TPL_INCLUDE_DIRS}) -link_directories(${Trilinos_LIBRARY_DIRS} ${Trilinos_TPL_LIBRARY_DIRS}) - -VertexCFD_add_tests( - MPI - LIBS ${Trilinos_LIBRARIES} ${Trilinos_TPL_LIBRARIES} - NAMES KokkosMPI EvaluatorTestHarness ) \ No newline at end of file diff --git a/src/test_harness/unit_test/tstEvaluatorTestHarness.cpp b/src/test_harness/unit_test/tstEvaluatorTestHarness.cpp deleted file mode 100644 index 1639b7b..0000000 --- a/src/test_harness/unit_test/tstEvaluatorTestHarness.cpp +++ /dev/null @@ -1,495 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Workset Data -template -struct TestData : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - PHX::MDField _node_coords; - PHX::MDField _ip_coords; - PHX::MDField _basis; - PHX::MDField - _grad_basis; - - TestData(const panzer::IntegrationRule& ir, - const panzer::BasisIRLayout& layout) - : _node_coords("node_coords", layout.functional_grad) - , _ip_coords("ip_coords", ir.dl_vector) - , _basis("basis", layout.basis) - , _grad_basis("grad_basis", layout.basis_grad) - { - this->addEvaluatedField(_node_coords); - this->addEvaluatedField(_ip_coords); - this->addEvaluatedField(_basis); - this->addEvaluatedField(_grad_basis); - this->setName("TestData"); - } - - void evaluateFields(typename panzer::Traits::EvalData workset) override - { - _node_coords.deep_copy(workset.int_rules[0]->node_coordinates); - _ip_coords.deep_copy(workset.int_rules[0]->ip_coordinates); - _basis.deep_copy(this->wda(workset).bases[0]->basis_scalar); - _grad_basis.deep_copy(this->wda(workset).bases[0]->grad_basis); - } -}; - -//---------------------------------------------------------------------------// -template -void test2D_default_cell() -{ - // Setup test fixture. - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - EXPECT_STREQ("Quadrilateral_4", test_fixture.cell_topo->getName()); - - // Create data. - auto test_eval = Teuchos::rcp(new TestData( - *test_fixture.ir, *test_fixture.basis_ir_layout)); - test_fixture.registerEvaluator(test_eval); - - // Set the time. - EXPECT_EQ(0.0, test_fixture.workset->time); - const double time = 1.3; - test_fixture.setTime(time); - EXPECT_EQ(time, test_fixture.workset->time); - - // Add required test fields. Check that some integration values were made - // as well as some basis values. - test_fixture.registerTestField(test_eval->_node_coords); - test_fixture.registerTestField(test_eval->_ip_coords); - test_fixture.registerTestField(test_eval->_basis); - test_fixture.registerTestField(test_eval->_grad_basis); - - // Evaluate fields. - test_fixture.evaluate(); - - // Get fields. - auto node_coords_result - = test_fixture.getTestFieldData(test_eval->_node_coords); - auto ip_coords_result - = test_fixture.getTestFieldData(test_eval->_ip_coords); - auto basis_result - = test_fixture.getTestFieldData(test_eval->_basis); - auto grad_basis_result - = test_fixture.getTestFieldData(test_eval->_grad_basis); - - // Check fields. - EXPECT_EQ(1, node_coords_result.extent(0)); - EXPECT_EQ(4, node_coords_result.extent(1)); - EXPECT_EQ(2, node_coords_result.extent(2)); - EXPECT_EQ(0.0, node_coords_result(0, 0, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 0, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 1, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 1, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 2, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 2, 1)); - EXPECT_EQ(0.0, node_coords_result(0, 3, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 3, 1)); - - EXPECT_EQ(1, test_fixture.numPoint()); - EXPECT_EQ(1, ip_coords_result.extent(0)); - EXPECT_EQ(1, ip_coords_result.extent(1)); - EXPECT_EQ(2, ip_coords_result.extent(2)); - EXPECT_EQ(0.5, ip_coords_result(0, 0, 0)); - EXPECT_EQ(0.5, ip_coords_result(0, 0, 1)); - - EXPECT_EQ(4, test_fixture.cardinality()); - EXPECT_EQ(1, basis_result.extent(0)); - EXPECT_EQ(4, basis_result.extent(1)); - EXPECT_EQ(1, basis_result.extent(2)); - EXPECT_EQ(0.25, basis_result(0, 0, 0)); - EXPECT_EQ(0.25, basis_result(0, 1, 0)); - EXPECT_EQ(0.25, basis_result(0, 2, 0)); - EXPECT_EQ(0.25, basis_result(0, 3, 0)); - - EXPECT_EQ(1, grad_basis_result.extent(0)); - EXPECT_EQ(4, grad_basis_result.extent(1)); - EXPECT_EQ(1, grad_basis_result.extent(2)); - EXPECT_EQ(2, grad_basis_result.extent(3)); - EXPECT_EQ(-0.5, grad_basis_result(0, 0, 0, 0)); - EXPECT_EQ(-0.5, grad_basis_result(0, 0, 0, 1)); - EXPECT_EQ(0.5, grad_basis_result(0, 1, 0, 0)); - EXPECT_EQ(-0.5, grad_basis_result(0, 1, 0, 1)); - EXPECT_EQ(0.5, grad_basis_result(0, 2, 0, 0)); - EXPECT_EQ(0.5, grad_basis_result(0, 2, 0, 1)); - EXPECT_EQ(-0.5, grad_basis_result(0, 3, 0, 0)); - EXPECT_EQ(0.5, grad_basis_result(0, 3, 0, 1)); -} - -//---------------------------------------------------------------------------// -template -void test3D_default_cell() -{ - // Setup test fixture. - const int num_space_dim = 3; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - EXPECT_STREQ("Hexahedron_8", test_fixture.cell_topo->getName()); - - // Create data. - auto test_eval = Teuchos::rcp(new TestData( - *test_fixture.ir, *test_fixture.basis_ir_layout)); - test_fixture.registerEvaluator(test_eval); - - // Set the time. - EXPECT_EQ(0.0, test_fixture.workset->time); - const double time = 1.3; - test_fixture.setTime(time); - EXPECT_EQ(time, test_fixture.workset->time); - - // Add required test fields. Check that some integration values were made - // as well as some basis values. - test_fixture.registerTestField(test_eval->_node_coords); - test_fixture.registerTestField(test_eval->_ip_coords); - test_fixture.registerTestField(test_eval->_basis); - test_fixture.registerTestField(test_eval->_grad_basis); - - // Evaluate fields. - test_fixture.evaluate(); - - // Get fields. - auto node_coords_result - = test_fixture.getTestFieldData(test_eval->_node_coords); - auto ip_coords_result - = test_fixture.getTestFieldData(test_eval->_ip_coords); - auto basis_result - = test_fixture.getTestFieldData(test_eval->_basis); - auto grad_basis_result - = test_fixture.getTestFieldData(test_eval->_grad_basis); - - // Check fields. - EXPECT_EQ(1, node_coords_result.extent(0)); - EXPECT_EQ(8, node_coords_result.extent(1)); - EXPECT_EQ(3, node_coords_result.extent(2)); - EXPECT_EQ(0.0, node_coords_result(0, 0, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 0, 1)); - EXPECT_EQ(0.0, node_coords_result(0, 0, 2)); - EXPECT_EQ(1.0, node_coords_result(0, 1, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 1, 1)); - EXPECT_EQ(0.0, node_coords_result(0, 1, 2)); - EXPECT_EQ(1.0, node_coords_result(0, 2, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 2, 1)); - EXPECT_EQ(0.0, node_coords_result(0, 2, 2)); - EXPECT_EQ(0.0, node_coords_result(0, 3, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 3, 1)); - EXPECT_EQ(0.0, node_coords_result(0, 3, 2)); - EXPECT_EQ(0.0, node_coords_result(0, 4, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 4, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 4, 2)); - EXPECT_EQ(1.0, node_coords_result(0, 5, 0)); - EXPECT_EQ(0.0, node_coords_result(0, 5, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 5, 2)); - EXPECT_EQ(1.0, node_coords_result(0, 6, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 6, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 6, 2)); - EXPECT_EQ(0.0, node_coords_result(0, 7, 0)); - EXPECT_EQ(1.0, node_coords_result(0, 7, 1)); - EXPECT_EQ(1.0, node_coords_result(0, 7, 2)); - - EXPECT_EQ(1, test_fixture.numPoint()); - EXPECT_EQ(1, ip_coords_result.extent(0)); - EXPECT_EQ(1, ip_coords_result.extent(1)); - EXPECT_EQ(3, ip_coords_result.extent(2)); - EXPECT_EQ(0.5, ip_coords_result(0, 0, 0)); - EXPECT_EQ(0.5, ip_coords_result(0, 0, 1)); - EXPECT_EQ(0.5, ip_coords_result(0, 0, 2)); - - EXPECT_EQ(8, test_fixture.cardinality()); - EXPECT_EQ(1, basis_result.extent(0)); - EXPECT_EQ(8, basis_result.extent(1)); - EXPECT_EQ(1, basis_result.extent(2)); - EXPECT_EQ(0.125, basis_result(0, 0, 0)); - EXPECT_EQ(0.125, basis_result(0, 1, 0)); - EXPECT_EQ(0.125, basis_result(0, 2, 0)); - EXPECT_EQ(0.125, basis_result(0, 3, 0)); - EXPECT_EQ(0.125, basis_result(0, 4, 0)); - EXPECT_EQ(0.125, basis_result(0, 5, 0)); - EXPECT_EQ(0.125, basis_result(0, 6, 0)); - EXPECT_EQ(0.125, basis_result(0, 7, 0)); - - EXPECT_EQ(1, grad_basis_result.extent(0)); - EXPECT_EQ(8, grad_basis_result.extent(1)); - EXPECT_EQ(1, grad_basis_result.extent(2)); - EXPECT_EQ(3, grad_basis_result.extent(3)); - EXPECT_EQ(-0.25, grad_basis_result(0, 0, 0, 0)); - EXPECT_EQ(-0.25, grad_basis_result(0, 0, 0, 1)); - EXPECT_EQ(-0.25, grad_basis_result(0, 0, 0, 2)); - EXPECT_EQ(0.25, grad_basis_result(0, 1, 0, 0)); - EXPECT_EQ(-0.25, grad_basis_result(0, 1, 0, 1)); - EXPECT_EQ(-0.25, grad_basis_result(0, 1, 0, 2)); - EXPECT_EQ(0.25, grad_basis_result(0, 2, 0, 0)); - EXPECT_EQ(0.25, grad_basis_result(0, 2, 0, 1)); - EXPECT_EQ(-0.25, grad_basis_result(0, 2, 0, 2)); - EXPECT_EQ(-0.25, grad_basis_result(0, 3, 0, 0)); - EXPECT_EQ(0.25, grad_basis_result(0, 3, 0, 1)); - EXPECT_EQ(-0.25, grad_basis_result(0, 3, 0, 2)); - EXPECT_EQ(-0.25, grad_basis_result(0, 4, 0, 0)); - EXPECT_EQ(-0.25, grad_basis_result(0, 4, 0, 1)); - EXPECT_EQ(0.25, grad_basis_result(0, 4, 0, 2)); - EXPECT_EQ(0.25, grad_basis_result(0, 5, 0, 0)); - EXPECT_EQ(-0.25, grad_basis_result(0, 5, 0, 1)); - EXPECT_EQ(0.25, grad_basis_result(0, 5, 0, 2)); - EXPECT_EQ(0.25, grad_basis_result(0, 6, 0, 0)); - EXPECT_EQ(0.25, grad_basis_result(0, 6, 0, 1)); - EXPECT_EQ(0.25, grad_basis_result(0, 6, 0, 2)); - EXPECT_EQ(-0.25, grad_basis_result(0, 7, 0, 0)); - EXPECT_EQ(0.25, grad_basis_result(0, 7, 0, 1)); - EXPECT_EQ(0.25, grad_basis_result(0, 7, 0, 2)); -} - -//---------------------------------------------------------------------------// -template -void test_custom_cell() -{ - // Setup test fixture. - const int num_cell = 1; - const int nodes_per_cell = 3; - const int num_space_dim = 2; - - EvaluatorTestFixture::host_coords_view coords( - "coords", num_cell, nodes_per_cell, num_space_dim); - coords(0, 0, 0) = 0.0; - coords(0, 0, 1) = 0.0; - - coords(0, 1, 0) = 1.0; - coords(0, 1, 1) = 0.0; - - coords(0, 2, 0) = 0.0; - coords(0, 2, 1) = 1.0; - - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - shards::getCellTopologyData>(), - coords, - integration_order, - basis_order); - - EXPECT_STREQ("Triangle_3", test_fixture.cell_topo->getName()); - - // Create data. - auto test_eval = Teuchos::rcp(new TestData( - *test_fixture.ir, *test_fixture.basis_ir_layout)); - test_fixture.registerEvaluator(test_eval); - - // Set the time. - EXPECT_EQ(0.0, test_fixture.workset->time); - const double time = 1.3; - test_fixture.setTime(time); - EXPECT_EQ(time, test_fixture.workset->time); - - // Add required test fields. Check that some integration values were made - // as well as some basis values. - test_fixture.registerTestField(test_eval->_node_coords); - test_fixture.registerTestField(test_eval->_ip_coords); - test_fixture.registerTestField(test_eval->_basis); - test_fixture.registerTestField(test_eval->_grad_basis); - - // Evaluate fields. - test_fixture.evaluate(); - - // Get fields. - auto node_coords_result - = test_fixture.getTestFieldData(test_eval->_node_coords); - auto ip_coords_result - = test_fixture.getTestFieldData(test_eval->_ip_coords); - auto basis_result - = test_fixture.getTestFieldData(test_eval->_basis); - auto grad_basis_result - = test_fixture.getTestFieldData(test_eval->_grad_basis); - - // Check fields. - EXPECT_EQ(num_cell, node_coords_result.extent(0)); - EXPECT_EQ(nodes_per_cell, node_coords_result.extent(1)); - EXPECT_EQ(num_space_dim, node_coords_result.extent(2)); - for (int cell = 0; cell < num_cell; ++cell) - { - for (int node = 0; node < nodes_per_cell; ++node) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - EXPECT_EQ(coords(cell, node, dim), - node_coords_result(cell, node, dim)); - } - } - } - - EXPECT_EQ(1, test_fixture.numPoint()); - - EXPECT_EQ(1, ip_coords_result.extent(0)); - EXPECT_EQ(1, ip_coords_result.extent(1)); - EXPECT_EQ(2, ip_coords_result.extent(2)); - EXPECT_DOUBLE_EQ(1.0 / 3.0, ip_coords_result(0, 0, 0)); - EXPECT_DOUBLE_EQ(1.0 / 3.0, ip_coords_result(0, 0, 1)); - - EXPECT_EQ(3, test_fixture.cardinality()); - EXPECT_EQ(1, basis_result.extent(0)); - EXPECT_EQ(3, basis_result.extent(1)); - EXPECT_EQ(1, basis_result.extent(2)); - EXPECT_DOUBLE_EQ(1.0 / 3.0, basis_result(0, 0, 0)); - EXPECT_DOUBLE_EQ(1.0 / 3.0, basis_result(0, 1, 0)); - EXPECT_DOUBLE_EQ(1.0 / 3.0, basis_result(0, 2, 0)); - - EXPECT_EQ(1, grad_basis_result.extent(0)); - EXPECT_EQ(3, grad_basis_result.extent(1)); - EXPECT_EQ(1, grad_basis_result.extent(2)); - EXPECT_EQ(2, grad_basis_result.extent(3)); - EXPECT_EQ(-1.0, grad_basis_result(0, 0, 0, 0)); - EXPECT_EQ(-1.0, grad_basis_result(0, 0, 0, 1)); - EXPECT_EQ(1.0, grad_basis_result(0, 1, 0, 0)); - EXPECT_EQ(0.0, grad_basis_result(0, 1, 0, 1)); - EXPECT_EQ(0.0, grad_basis_result(0, 2, 0, 0)); - EXPECT_EQ(1.0, grad_basis_result(0, 2, 0, 1)); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, residual_test_2d_default_cell) -{ - test2D_default_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, jacobian_test_2d_default_cell) -{ - test2D_default_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, residual_test_3d_default_cell) -{ - test3D_default_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, jacobian_test_3d_default_cell) -{ - test3D_default_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, residual_test_custom_cell) -{ - test_custom_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, jacobian_test_custom_cell) -{ - test_custom_cell(); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, default_cell_bad_space_dim) -{ - // Setup test fixture. - const int num_space_dim = 4; - const int integration_order = 1; - const int basis_order = 1; - - const std::string msg - = "Invalid spatial dimensions (4): must be 1, 2, or 3."; - EXPECT_THROW( - try { - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - } catch (const std::logic_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::logic_error); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, custom_cell_bad_space_dim) -{ - // Setup test fixture. - const int num_cell = 1; - const int nodes_per_cell = 3; - const int num_space_dim = 3; - EvaluatorTestFixture::host_coords_view coords( - "coords", num_cell, nodes_per_cell, num_space_dim); - const int integration_order = 1; - const int basis_order = 1; - - const std::string msg - = "Unexpected spatial dimensions in provided coordinates: " - "Triangle_3 expects 2 dimensions, but 3 were provided."; - EXPECT_THROW( - try { - EvaluatorTestFixture test_fixture( - shards::getCellTopologyData>(), - coords, - integration_order, - basis_order); - } catch (const std::logic_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::logic_error); -} - -//---------------------------------------------------------------------------// -TEST(EvaluatorTestHarness, custom_cell_bad_node_count) -{ - // Setup test fixture. - const int num_cell = 1; - const int nodes_per_cell = 4; - const int num_space_dim = 2; - EvaluatorTestFixture::host_coords_view coords( - "coords", num_cell, nodes_per_cell, num_space_dim); - const int integration_order = 1; - const int basis_order = 1; - - const std::string msg - = "Unexpected node count in provided coordinates: " - "Triangle_3 expects 3 nodes, but 4 were provided."; - EXPECT_THROW( - try { - EvaluatorTestFixture test_fixture( - shards::getCellTopologyData>(), - coords, - integration_order, - basis_order); - } catch (const std::logic_error& e) { - EXPECT_EQ(msg, e.what()); - throw; - }, - std::logic_error); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/test_harness/unit_test/tstKokkosMPI.cpp b/src/test_harness/unit_test/tstKokkosMPI.cpp deleted file mode 100644 index f092437..0000000 --- a/src/test_harness/unit_test/tstKokkosMPI.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#include - -#include - -#include - -#include - -namespace Test -{ -//---------------------------------------------------------------------------// -void kokkosTest() -{ - int size = 10; - Kokkos::View data("data", size); - Kokkos::parallel_for( - "fill_data", - Kokkos::RangePolicy(0, size), - KOKKOS_LAMBDA(const int i) { data(i) = 1.0; }); - - auto data_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), data); - int sum = 0; - for (int i = 0; i < size; ++i) - sum += data_host(i); - - MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - - int comm_size; - MPI_Comm_size(MPI_COMM_WORLD, &comm_size); - - EXPECT_EQ(comm_size * size, sum); -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(kokkos_mpi, kokkos_mpi_test) -{ - kokkosTest(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.cpp deleted file mode 100644 index 30a566a..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp" -#include "VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::TurbulenceBoundaryEddyViscosity) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp deleted file mode 100644 index c3c8540..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Populates boundary eddy viscosity fields according to boundary condition -// type -//---------------------------------------------------------------------------// -template -class TurbulenceBoundaryEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - TurbulenceBoundaryEddyViscosity(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string& flux_prefix); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_nu_t; - - private: - PHX::MDField _interior_nu_t; - PHX::MDField _wall_func_nu_t; - - bool _wall_func; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity_impl.hpp deleted file mode 100644 index 3555e6e..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity_impl.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -TurbulenceBoundaryEddyViscosity::TurbulenceBoundaryEddyViscosity( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string& flux_prefix) - : _boundary_nu_t(flux_prefix + "turbulent_eddy_viscosity", ir.dl_scalar) - , _interior_nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , _wall_func_nu_t("wall_func_turbulent_eddy_viscosity", ir.dl_scalar) - , _wall_func(false) -{ - // Only check boundary if parameter list is populated - // (it should be empty in some models, i.e. WALE) - if (bc_params.numParams() > 0) - { - // Check boundary condition type is a wall function - const std::string bc_type = bc_params.get("Type"); - - if (std::string::npos != bc_type.find("Wall Function")) - { - _wall_func = true; - } - } - - // Add evaluated fields - this->addEvaluatedField(_boundary_nu_t); - - // Add dependent fields - if (_wall_func) - { - this->addDependentField(_wall_func_nu_t); - } - else - { - this->addDependentField(_interior_nu_t); - } - - this->setName("Boundary State Turbulence Eddy Viscosity"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceBoundaryEddyViscosity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceBoundaryEddyViscosity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _boundary_nu_t.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Set boundary nu_t according to BC type - if (_wall_func) - { - _boundary_nu_t(cell, point) = _wall_func_nu_t(cell, point); - } - else - { - _boundary_nu_t(cell, point) = _interior_nu_t(cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCEBOUNDARYEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.cpp deleted file mode 100644 index d798e68..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp" -#include "VertexCFD_BoundaryState_TurbulenceExtrapolate_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::TurbulenceExtrapolate) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp deleted file mode 100644 index fda293e..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class TurbulenceExtrapolate : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - TurbulenceExtrapolate(const panzer::IntegrationRule& ir, - const std::string variable_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_variable; - - PHX::MDField - _boundary_grad_variable; - - private: - PHX::MDField _variable; - PHX::MDField - _grad_variable; - - int _num_grad_dim; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate_impl.hpp deleted file mode 100644 index 20ec8da..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate_impl.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This class should be used for extrapolate boundary conditions. -//---------------------------------------------------------------------------// -template -TurbulenceExtrapolate::TurbulenceExtrapolate( - const panzer::IntegrationRule& ir, const std::string variable_name) - : _boundary_variable("BOUNDARY_" + variable_name, ir.dl_scalar) - , _boundary_grad_variable("BOUNDARY_GRAD_" + variable_name, ir.dl_vector) - , _variable(variable_name, ir.dl_scalar) - , _grad_variable("GRAD_" + variable_name, ir.dl_vector) - , _num_grad_dim(ir.spatial_dimension) -{ - // Add evaluated fields - this->addEvaluatedField(_boundary_variable); - this->addEvaluatedField(_boundary_grad_variable); - - // Add dependent fields - this->addDependentField(_variable); - this->addDependentField(_grad_variable); - - this->setName(variable_name - + " Boundary State Turbulence Model Extrapolate " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceExtrapolate::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceExtrapolate::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_variable.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Assign boundary values - _boundary_variable(cell, point) = _variable(cell, point); - - // Assign gradient - for (int d = 0; d < _num_grad_dim; ++d) - { - _boundary_grad_variable(cell, point, d) - = _grad_variable(cell, point, d); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCEEXTRAPOLATE_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.cpp deleted file mode 100644 index ad3e1b1..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceFixed.hpp" -#include "VertexCFD_BoundaryState_TurbulenceFixed_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::TurbulenceFixed) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp deleted file mode 100644 index f3d8522..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class TurbulenceFixed : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - TurbulenceFixed(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string variable_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_variable; - - PHX::MDField - _boundary_grad_variable; - - private: - PHX::MDField - _grad_variable; - - double _fixed_value; - int _num_grad_dim; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed_impl.hpp deleted file mode 100644 index 6c8cb11..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed_impl.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// This function should be used for fixed boundary conditions. -//---------------------------------------------------------------------------// -template -TurbulenceFixed::TurbulenceFixed( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string variable_name) - : _boundary_variable("BOUNDARY_" + variable_name, ir.dl_scalar) - , _boundary_grad_variable("BOUNDARY_GRAD_" + variable_name, ir.dl_vector) - , _grad_variable("GRAD_" + variable_name, ir.dl_vector) - , _fixed_value(bc_params.get(variable_name + " Value")) - , _num_grad_dim(ir.spatial_dimension) -{ - // Add evaluated fields - this->addEvaluatedField(_boundary_variable); - this->addEvaluatedField(_boundary_grad_variable); - - // Add dependent fields - this->addDependentField(_grad_variable); - - this->setName(variable_name + " Boundary State Turbulence Model Fixed " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceFixed::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceFixed::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_variable.extent(1); - - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, num_point), - [&](const int point) { - // Assign boundary values - _boundary_variable(cell, point) = _fixed_value; - - // Assign gradient. - for (int d = 0; d < _num_grad_dim; ++d) - { - _boundary_grad_variable(cell, point, d) - = _grad_variable(cell, point, d); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCEFIXED_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.cpp deleted file mode 100644 index 2fcbc58..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp" -#include "VertexCFD_BoundaryState_TurbulenceInletOutlet_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::TurbulenceInletOutlet) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp deleted file mode 100644 index c387c67..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class TurbulenceInletOutlet : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - TurbulenceInletOutlet(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string variable_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_variable; - - PHX::MDField - _boundary_grad_variable; - - private: - PHX::MDField _variable; - PHX::MDField - _grad_variable; - PHX::MDField - _normals; - Kokkos::Array, - num_space_dim> - _velocity; - - double _inlet_value; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet_impl.hpp deleted file mode 100644 index 264518c..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet_impl.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Inlet/outlet boundary condition for turbulence quantities -//---------------------------------------------------------------------------// -template -TurbulenceInletOutlet::TurbulenceInletOutlet( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const std::string variable_name) - : _boundary_variable("BOUNDARY_" + variable_name, ir.dl_scalar) - , _boundary_grad_variable("BOUNDARY_GRAD_" + variable_name, ir.dl_vector) - , _variable(variable_name, ir.dl_scalar) - , _grad_variable("GRAD_" + variable_name, ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _inlet_value(bc_params.get(variable_name + " Inlet Value")) -{ - // Add evaluated fields - this->addEvaluatedField(_boundary_variable); - this->addEvaluatedField(_boundary_grad_variable); - - // Add dependent fields - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - this->addDependentField(_variable); - this->addDependentField(_grad_variable); - this->addDependentField(_normals); - - this->setName(variable_name - + " Boundary State Turbulence Model Inlet/Outlet " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceInletOutlet::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceInletOutlet::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_variable.extent(1); - const int num_grad_dim = _grad_variable.extent(2); - const double smooth_ramp = 1.0e-8; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Compute \vec{vel} \cdot \vec{n} - scalar_type vel_dot_n = 0.0; - for (int dim = 0; dim < num_grad_dim; ++dim) - { - vel_dot_n += _velocity[dim](cell, point) - * _normals(cell, point, dim); - } - - // Ramping function for inlet/outlet - const scalar_type outlet - = SmoothMath::ramp(vel_dot_n, -smooth_ramp, smooth_ramp); - - // Assign boundary values - _boundary_variable(cell, point) = (1.0 - outlet) * _inlet_value - + outlet * _variable(cell, point); - - // Assign gradient - for (int d = 0; d < num_grad_dim; ++d) - { - _boundary_grad_variable(cell, point, d) - = _grad_variable(cell, point, d); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCEINLETOUTLET_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.cpp deleted file mode 100644 index 1dee5bf..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp" -#include "VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::TurbulenceKEpsilonWallFunction) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp deleted file mode 100644 index 51f95d3..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Wall function boundary conditions for K-Epsilon family of turbulence -// models as outlined by Kuzmin et al. (2007) -//---------------------------------------------------------------------------// -template -class TurbulenceKEpsilonWallFunction - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - TurbulenceKEpsilonWallFunction( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_k; - PHX::MDField _boundary_e; - - PHX::MDField - _boundary_grad_k; - - PHX::MDField - _boundary_grad_e; - - PHX::MDField _boundary_u_tau; - PHX::MDField _boundary_y_plus; - PHX::MDField _wall_func_nu_t; - - private: - PHX::MDField _k; - PHX::MDField _e; - Kokkos::Array, - num_space_dim> - _velocity; - PHX::MDField - _grad_k; - PHX::MDField - _grad_e; - PHX::MDField - _normals; - - int _num_grad_dim; - double _C_mu; - double _nu; - double _kappa; - double _yp_tr; - bool _neumann; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction_impl.hpp deleted file mode 100644 index f99c296..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction_impl.hpp +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -TurbulenceKEpsilonWallFunction:: - TurbulenceKEpsilonWallFunction( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _boundary_k("BOUNDARY_turb_kinetic_energy", ir.dl_scalar) - , _boundary_e("BOUNDARY_turb_dissipation_rate", ir.dl_scalar) - , _boundary_grad_k("BOUNDARY_GRAD_turb_kinetic_energy", ir.dl_vector) - , _boundary_grad_e("BOUNDARY_GRAD_turb_dissipation_rate", ir.dl_vector) - , _boundary_u_tau("BOUNDARY_friction_velocity", ir.dl_scalar) - , _boundary_y_plus("BOUNDARY_y_plus", ir.dl_scalar) - , _wall_func_nu_t("wall_func_turbulent_eddy_viscosity", ir.dl_scalar) - , _k("turb_kinetic_energy", ir.dl_scalar) - , _e("turb_dissipation_rate", ir.dl_scalar) - , _grad_k("GRAD_turb_kinetic_energy", ir.dl_vector) - , _grad_e("GRAD_turb_dissipation_rate", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _C_mu(0.09) - , _nu(fluid_prop.constantKinematicViscosity()) - , _kappa(0.41) - , _yp_tr(11.06) - , _neumann(false) -{ - // Check for epsilon boundary specification - if (bc_params.isType("Epsilon Condition Type")) - { - const std::string bc_type - = bc_params.get("Epsilon Condition Type"); - - if (bc_type == "Neumann") - { - _neumann = true; - } - else if (bc_type == "Dirichlet") - { - _neumann = false; - } - else - { - std::string msg = "Unknown Epsilon Condition Type " + bc_type; - msg += "\nPlease choose from Dirichlet (default) or Neumann.\n"; - - throw std::runtime_error(msg); - } - } - - // Add evaluated fields - this->addEvaluatedField(_boundary_k); - this->addEvaluatedField(_boundary_e); - this->addEvaluatedField(_boundary_grad_k); - this->addEvaluatedField(_boundary_grad_e); - this->addEvaluatedField(_boundary_u_tau); - this->addEvaluatedField(_boundary_y_plus); - this->addEvaluatedField(_wall_func_nu_t); - - // Add dependent fields - this->addDependentField(_k); - this->addDependentField(_e); - this->addDependentField(_grad_k); - this->addDependentField(_grad_e); - this->addDependentField(_normals); - Utils::addDependentVectorField(*this, ir.dl_scalar, _velocity, "velocity_"); - - this->setName("Boundary State Turbulence Model K-Epsilon Wall Function " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceKEpsilonWallFunction::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceKEpsilonWallFunction::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_k.extent(1); - const double max_tol = 1e-10; - - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Calculate velocity magnitude - scalar_type mag_u = 0.0; - - for (int d = 0; d < num_space_dim; d++) - { - mag_u += pow(_velocity[d](cell, point), 2.0); - } - - mag_u = pow(SmoothMath::max(mag_u, max_tol, 0.0), 0.5); - - // Set turbulent kinetic energy boundary using Neumann condition - _boundary_k(cell, point) = _k(cell, point); - - for (int d = 0; d < num_space_dim; ++d) - { - _boundary_grad_k(cell, point, d) = _grad_k(cell, point, d); - - // Subtract boundary normal component - for (int grad_dim = 0; grad_dim < num_space_dim; ++grad_dim) - { - _boundary_grad_k(cell, point, d) - -= _grad_k(cell, point, grad_dim) - * _normals(cell, point, grad_dim) - * _normals(cell, point, d); - } - } - - // Calculate friction velocity and nu_t at the wall as suggested - // by Kuzmin et al. (2007) - const scalar_type u_tau = SmoothMath::max( - pow(_C_mu, 0.25) - * pow(SmoothMath::max(_k(cell, point), max_tol, 0.0), 0.5), - mag_u / _yp_tr, - 0.0); - - const double nu_t_w = _kappa * _yp_tr * _nu; - - // Set epsilon and nu_t at boundary according to user setting - if (_neumann) - { - // Set epsilon boundary equal to interior value - _boundary_e(cell, point) = _e(cell, point); - - // Calculate boundary gradient - for (int d = 0; d < num_space_dim; ++d) - { - _boundary_grad_e(cell, point, d) - = _grad_e(cell, point, d) - + (_kappa * pow(u_tau, 5.0) / pow(nu_t_w, 2.0)) - * _normals(cell, point, d); - - // Subtract interior boundary normal component - for (int grad_dim = 0; grad_dim < num_space_dim; ++grad_dim) - { - _boundary_grad_e(cell, point, d) - -= _grad_e(cell, point, grad_dim) - * _normals(cell, point, grad_dim) - * _normals(cell, point, d); - } - } - - // Set boundary nu_t to analytical solution - _wall_func_nu_t(cell, point) = nu_t_w; - } - else - { - // Calculate epsilon boundary value - _boundary_e(cell, point) = pow(u_tau, 4.0) / nu_t_w; - - // Set boundary gradient equal to interior gradient - for (int d = 0; d < num_space_dim; ++d) - { - _boundary_grad_e(cell, point, d) = _grad_e(cell, point, d); - } - - // Calculate boundary nu_t with standard k-epsilon form - _wall_func_nu_t(cell, point) - = _C_mu * pow(_k(cell, point), 2.0) - / SmoothMath::max(_e(cell, point), max_tol, 0.0); - } - - // Fill in boundary friction velocity and y+ fields for - // post-processing - _boundary_u_tau(cell, point) = u_tau; - _boundary_y_plus(cell, point) = _yp_tr; - - // TODO: define TKE production term at boundary to enforce P = D? - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCEKEPSILONWALLFUNCTION_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.cpp deleted file mode 100644 index 61a91d9..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_BoundaryState_TurbulenceSymmetry.hpp" -#include "VertexCFD_BoundaryState_TurbulenceSymmetry_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::BoundaryCondition::TurbulenceSymmetry) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp deleted file mode 100644 index 9770aa8..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class TurbulenceSymmetry : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - TurbulenceSymmetry(const panzer::IntegrationRule& ir, - const std::string variable_name); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - public: - PHX::MDField _boundary_variable; - - PHX::MDField - _boundary_grad_variable; - - private: - PHX::MDField _variable; - PHX::MDField - _grad_variable; - PHX::MDField - _normals; - - int _num_grad_dim; -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry_impl.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry_impl.hpp deleted file mode 100644 index 19cd230..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry_impl.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_IMPL_HPP -#define VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -// Symmetry boundary condition for turbulence quantities -//---------------------------------------------------------------------------// -template -TurbulenceSymmetry::TurbulenceSymmetry( - const panzer::IntegrationRule& ir, const std::string variable_name) - : _boundary_variable("BOUNDARY_" + variable_name, ir.dl_scalar) - , _boundary_grad_variable("BOUNDARY_GRAD_" + variable_name, ir.dl_vector) - , _variable(variable_name, ir.dl_scalar) - , _grad_variable("GRAD_" + variable_name, ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _num_grad_dim(ir.spatial_dimension) -{ - // Add evaluated fields - this->addEvaluatedField(_boundary_variable); - this->addEvaluatedField(_boundary_grad_variable); - - // Add dependent fields - this->addDependentField(_variable); - this->addDependentField(_grad_variable); - this->addDependentField(_normals); - - this->setName(variable_name + " Boundary State Turbulence Model Symmetry " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceSymmetry::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void TurbulenceSymmetry::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _grad_variable.extent(1); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Assign boundary values - _boundary_variable(cell, point) = _variable(cell, point); - - // Compute boundary normal gradient - scalar_type grad_var_dot_n = 0.0; - - for (int d = 0; d < _num_grad_dim; ++d) - { - grad_var_dot_n += _grad_variable(cell, point, d) - * _normals(cell, point, d); - } - - // Assign gradient - for (int d = 0; d < _num_grad_dim; ++d) - { - _boundary_grad_variable(cell, point, d) - = _grad_variable(cell, point, d) - - grad_var_dot_n * _normals(cell, point, d); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_BOUNDARYSTATE_TURBULENCESYMMETRY_IMPL_HPP diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.cpp b/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.cpp deleted file mode 100644 index 2b1108a..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::BoundaryCondition::TurbulenceBoundaryStateFactory) diff --git a/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp b/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp deleted file mode 100644 index 3cf6440..0000000 --- a/src/turbulence_models/boundary_conditions/VertexCFD_TurbulenceBoundaryState_Factory.hpp +++ /dev/null @@ -1,292 +0,0 @@ -#ifndef VERTEXCFD_TURBULENCEBOUNDARYSTATE_FACTORY_HPP -#define VERTEXCFD_TURBULENCEBOUNDARYSTATE_FACTORY_HPP - -#include "closure_models/VertexCFD_Closure_ElementLength.hpp" -#include "closure_models/VertexCFD_Closure_MeasureElementLength.hpp" -#include "closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp" -#include "closure_models/VertexCFD_Closure_SingularValueElementLength.hpp" - -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp" - -#include - -#include -#include - -namespace VertexCFD -{ -namespace BoundaryCondition -{ -//---------------------------------------------------------------------------// -template -class TurbulenceBoundaryStateFactory -{ - public: - static std::vector>> - create(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& bc_params, - const Teuchos::ParameterList& user_params, - const std::string _turbulence_model_name, - const FluidProperties::ConstantFluidProperties& fluid_prop) - { - // Evaluator vector to return - std::vector>> evaluators; - - // Adding diffusivity coefficient closure model and initializing - // `turb_field_names_vct` for each equation of the turbulence model - std::vector turb_field_names_vct; - if (_turbulence_model_name != "No Turbulence Model") - { - // Spalart-Allmaras model family - if (std::string::npos - != _turbulence_model_name.find("Spalart-Allmaras")) - { - // Variable name - turb_field_names_vct.push_back("spalart_allmaras_variable"); - - // Diffusive coefficient - const auto diffusive_coeff_op = Teuchos::rcp( - new ClosureModel::IncompressibleSpalartAllmarasDiffusivityCoefficient< - EvalType, - panzer::Traits>(ir, fluid_prop)); - evaluators.push_back(diffusive_coeff_op); - - // Turbulent eddy viscosity - const auto eddy_visc_op = Teuchos::rcp( - new ClosureModel::IncompressibleSpalartAllmarasEddyViscosity< - EvalType, - panzer::Traits>(ir, fluid_prop)); - evaluators.push_back(eddy_visc_op); - } - // K-Epsilon model family - else if (std::string::npos - != _turbulence_model_name.find("K-Epsilon")) - { - // K-Epsilon turbulence variable names - turb_field_names_vct.push_back("turb_kinetic_energy"); - turb_field_names_vct.push_back("turb_dissipation_rate"); - - // Realizable K-Epsilon closure models - if (std::string::npos - != _turbulence_model_name.find("Realizable K-Epsilon")) - { - // Diffusive coefficient with non-standard coefficients - const auto diffusive_coeff_op = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonDiffusivityCoefficient< - EvalType, - panzer::Traits>( - ir, fluid_prop, 1.0, 1.2, "BOUNDARY_")); - evaluators.push_back(diffusive_coeff_op); - - // Turbulent eddy viscosity - const auto eddy_visc_op = Teuchos::rcp( - new ClosureModel::IncompressibleRealizableKEpsilonEddyViscosity< - EvalType, - panzer::Traits, - NumSpaceDim>(ir)); - evaluators.push_back(eddy_visc_op); - } - // Standard K-Epsilon closure models - else - { - // Diffusive coefficient - const auto diffusive_coeff_op = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonDiffusivityCoefficient< - EvalType, - panzer::Traits>( - ir, fluid_prop, 1.0, 1.3, "BOUNDARY_")); - evaluators.push_back(diffusive_coeff_op); - - // Turbulent eddy viscosity - const auto eddy_visc_op = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonEddyViscosity< - EvalType, - panzer::Traits>(ir)); - evaluators.push_back(eddy_visc_op); - } - } - // WALE algebraic LES model - else if (std::string::npos != _turbulence_model_name.find("WALE")) - { - // Sub-grid eddy viscosity - const auto eddy_visc_op = Teuchos::rcp( - new ClosureModel::IncompressibleWALEEddyViscosity< - EvalType, - panzer::Traits, - NumSpaceDim>(ir, user_params)); - evaluators.push_back(eddy_visc_op); - - // Delta (element length) closure model - const std::string delta_prefix = "les_"; - const auto turb_params - = user_params.isSublist("Turbulence Parameters") - ? user_params.sublist("Turbulence Parameters") - : Teuchos::ParameterList(); - const std::string delta_type - = turb_params.isType("LES Element Length") - ? turb_params.get("LES Element Length") - : "ElementLength"; - - if (delta_type == "ElementLength") - { - auto eval_delta = Teuchos::rcp( - new ClosureModel::ElementLength( - ir, delta_prefix)); - - evaluators.push_back(eval_delta); - } - else if (delta_type == "MeasureElementLength") - { - auto eval_delta = Teuchos::rcp( - new ClosureModel::MeasureElementLength( - ir, delta_prefix)); - - evaluators.push_back(eval_delta); - } - else if (delta_type == "MetricTensorElementLength") - { - auto eval_delta = Teuchos::rcp( - new ClosureModel:: - MetricTensorElementLength( - ir, delta_prefix)); - - evaluators.push_back(eval_delta); - } - else if (delta_type == "SingularValueElementLength") - { - const auto method = turb_params.get( - "Element Length Method"); - - auto eval_delta = Teuchos::rcp( - new ClosureModel::SingularValueElementLength< - EvalType, - panzer::Traits>(ir, method, delta_prefix)); - - evaluators.push_back(eval_delta); - } - else - { - std::string msg = "Unknown Delta Closure Model:\n"; - - msg += delta_type; - msg += "\n"; - msg += "Please choose from:\n"; - msg += "ElementLength\n"; - msg += "MeasureElementLength\n"; - msg += "MetricTensorElementLength\n"; - msg += "SingularValueElementLength\n"; - - throw std::runtime_error(msg); - } - } - } - - // Loop over boundary conditions found in input file for each - // turbulence model - bool found_model = false; - if (bc_params.isType("Type") - && (turb_field_names_vct.size() > 0)) - { - const auto bc_type = bc_params.get("Type"); - if (bc_type == "Fixed") - { - // Fixed boundary condition - for (auto& variable_name : turb_field_names_vct) - { - const auto state - = Teuchos::rcp(new TurbulenceFixed( - ir, bc_params, variable_name)); - evaluators.push_back(state); - } - found_model = true; - } - - else if (bc_type == "Extrapolate") - { - // Extrapolate boundary condition - for (auto& variable_name : turb_field_names_vct) - { - const auto state = Teuchos::rcp( - new TurbulenceExtrapolate( - ir, variable_name)); - evaluators.push_back(state); - } - found_model = true; - } - - else if (bc_type == "InletOutlet") - { - // Inlet/outlet boundary condition - for (auto& variable_name : turb_field_names_vct) - { - const auto state = Teuchos::rcp( - new TurbulenceInletOutlet( - ir, bc_params, variable_name)); - evaluators.push_back(state); - } - found_model = true; - } - - else if (bc_type == "Symmetry") - { - // Symmetry boundary condition - for (auto& variable_name : turb_field_names_vct) - { - const auto state = Teuchos::rcp( - new TurbulenceSymmetry( - ir, variable_name)); - evaluators.push_back(state); - } - found_model = true; - } - - else if (bc_type == "K-Epsilon Wall Function") - { - // Wall functions for use with high Re K-Epsilon models - const auto state = Teuchos::rcp( - new TurbulenceKEpsilonWallFunction( - ir, bc_params, fluid_prop)); - evaluators.push_back(state); - - found_model = true; - } - - // Error message if model not found - if (!found_model) - { - std::string msg = "\n\nBoundary state " + bc_type - + " failed to build.\n"; - msg += "The boundary conditions implemented in VERTEX-CFD\n"; - msg += "for the turbulence model equations are:\n"; - msg += "Extrapolate,\n"; - msg += "Fixed,\n"; - msg += "InletOutlet,\n"; - msg += "K-Epsilon Wall Function,\n"; - msg += "Symmetry\n"; - msg += "\n"; - throw std::runtime_error(msg); - } - } - - // Return vector of evaluators - return evaluators; - } -}; - -//---------------------------------------------------------------------------// - -} // end namespace BoundaryCondition -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TURBULENCEBOUNDARYSTATE_FACTORY_HPP diff --git a/src/turbulence_models/boundary_conditions/unit_test/CMakeLists.txt b/src/turbulence_models/boundary_conditions/unit_test/CMakeLists.txt deleted file mode 100644 index ae3337f..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - TurbulenceBoundaryEddyViscosity - TurbulenceExtrapolate - TurbulenceFixed - TurbulenceInletOutlet - TurbulenceKEpsilonWallFunction - TurbulenceSymmetry - ) diff --git a/src/turbulence_models/boundary_conditions/unit_test/doc/KEpsilon_Wall_Function_reference.py b/src/turbulence_models/boundary_conditions/unit_test/doc/KEpsilon_Wall_Function_reference.py deleted file mode 100644 index 94b1622..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/doc/KEpsilon_Wall_Function_reference.py +++ /dev/null @@ -1,62 +0,0 @@ -import numpy as np - -vel = [1.5, -3.0, 4.5] -grad_k = [0.02, 0.04, 0.06] -grad_e = [0.03, 0.06, 0.09] -normals = [0.33, 0.66, 0.99] - -tke_list = [0.25, 2.5] -eps = 3.1 - -yplus = 11.06 -C_mu = 0.09 -nu = 1e-4 -kappa = 0.41 - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing K-Epsilon wall function BCs in ", dim, "D\n") - - vel_dim = vel[:dim] - normals_dim = normals[:dim] - grad_k_dim = grad_k[:dim] - grad_e_dim = grad_e[:dim] - - mag_u = np.linalg.norm(np.array(vel_dim)) - - print("\tVelocity magnitude: ", mag_u, "\n") - - print("\tLimiting tke = ", pow(mag_u / yplus / pow(C_mu, 0.25), 2.0), "\n") - - for tke in tke_list: - print("\tTesting tke value = ", tke, "\n") - - u_tau = max(pow(C_mu, 0.25) * pow(tke, 0.5), mag_u / yplus) - - print("\t\tu_tau = ", u_tau, "\n") - - nu_t = kappa * yplus * nu - - print("\t\tNeumann nu_t = ", nu_t, "\n") - - nu_t = C_mu * pow(tke, 2.0) / eps - - print("\t\tDirichlet nu_t = ", nu_t, "\n") - - e_bnd = pow(u_tau, 4.0) / nu_t - - print("\t\tboundary e for dirichlet condition: ", e_bnd) - - boundary_grad_k = grad_k_dim - np.array(np.dot( - grad_k_dim, normals_dim)) * normals_dim - boundary_grad_e = grad_e_dim + np.array(kappa * pow(u_tau, 5.0) / pow( - nu_t, 2.0) - np.dot(grad_e_dim, normals_dim)) * normals_dim - - for d in range(dim): - print("\t\tk boundary gradient component ", d, " = ", - boundary_grad_k[d], "\n") - print("\t\te boundary gradient component ", d, " = ", - boundary_grad_e[d], "\n") - - print("\n") diff --git a/src/turbulence_models/boundary_conditions/unit_test/doc/turbulence_symmetry_reference.py b/src/turbulence_models/boundary_conditions/unit_test/doc/turbulence_symmetry_reference.py deleted file mode 100644 index 8039bef..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/doc/turbulence_symmetry_reference.py +++ /dev/null @@ -1,21 +0,0 @@ -import numpy as np - -grad_var = [0.02, 0.04, 0.06] -normals = [0.33, 0.66, 0.99] - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing boundary variable gradient in ", dim, "D\n") - - normals_dim = normals[:dim] - grad_var_dim = grad_var[:dim] - - boundary_grad_var = grad_var_dim - np.array( - np.dot(grad_var_dim, normals_dim)) * normals_dim - - for d in range(dim): - print("Boundary gradient component ", d, " = ", boundary_grad_var[d], - "\n") - - print("\n") diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceBoundaryEddyViscosity.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceBoundaryEddyViscosity.cpp deleted file mode 100644 index ffb41e3..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceBoundaryEddyViscosity.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceBoundaryEddyViscosity.hpp" - -#include -#include -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _nu_t_int; - PHX::MDField _nu_t_wf; - - Dependencies(const panzer::IntegrationRule& ir) - : _nu_t_int("turbulent_eddy_viscosity", ir.dl_scalar) - , _nu_t_wf("wall_func_turbulent_eddy_viscosity", ir.dl_scalar) - { - this->addEvaluatedField(_nu_t_int); - this->addEvaluatedField(_nu_t_wf); - this->setName( - "Turbulence Model Boundary Eddy Viscosity Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _nu_t_int.deep_copy(2.5); - _nu_t_wf.deep_copy(3.5); - } -}; - -//---------------------------------------------------------------------------// -// Inlet/outlet cases -enum class EddyViscosity -{ - wall_func, - wall_modeled -}; - -//---------------------------------------------------------------------------// -template -void testEval(const EddyViscosity bc_type) -{ - // Test fixture - const int num_space_dim = 2; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set BC type - std::string type = ""; - - switch (bc_type) - { - case (EddyViscosity::wall_func): - type = "Wall Function Condition"; - break; - case (EddyViscosity::wall_modeled): - type = "Wall Modeled Condition"; - break; - } - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create boundary eddy viscosity evaluator. - Teuchos::ParameterList bc_params; - bc_params.set("Type", type); - const auto eddy_visc_eval = Teuchos::rcp( - new BoundaryCondition::TurbulenceBoundaryEddyViscosity( - *test_fixture.ir, bc_params, "BOUNDARY_")); - test_fixture.registerEvaluator(eddy_visc_eval); - - // Add required test fields. - test_fixture.registerTestField(eddy_visc_eval->_boundary_nu_t); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_nu_t_result = test_fixture.getTestFieldData( - eddy_visc_eval->_boundary_nu_t); - - const int num_point = boundary_nu_t_result.extent(1); - - const double exp_value = type == "Wall Function Condition" ? 3.5 : 2.5; - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_value, fieldValue(boundary_nu_t_result, 0, qp)); - } -} -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto bc_type = info.param; - switch (bc_type) - { - case (EddyViscosity::wall_func): - return "wall_func"; - case (EddyViscosity::wall_modeled): - return "wall_modeled"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// Residual evaluation -TEST_P(EvaluationTest, residual) -{ - EddyViscosity bc_type; - bc_type = GetParam(); - testEval(bc_type); -} - -// Jacobian evaluation -TEST_P(EvaluationTest, jacobian) -{ - EddyViscosity bc_type; - bc_type = GetParam(); - testEval(bc_type); -} - -//---------------------------------------------------------------------------// -// Generate test suite with wall function and wall modeled options -INSTANTIATE_TEST_SUITE_P(EddyViscosityBC, - EvaluationTest, - testing::Values(EddyViscosity::wall_func, - EddyViscosity::wall_modeled), - EvaluationTest::ParamNameGenerator{}); - -//---------------------------------------------------------------------------// -} // end namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceExtrapolate.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceExtrapolate.cpp deleted file mode 100644 index 6f43228..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceExtrapolate.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceExtrapolate.hpp" -#include - -#include -#include -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _variable; - - PHX::MDField - _grad_variable; - - Dependencies(const panzer::IntegrationRule& ir) - : _variable("variable", ir.dl_scalar) - , _grad_variable("GRAD_variable", ir.dl_vector) - { - this->addEvaluatedField(_variable); - this->addEvaluatedField(_grad_variable); - this->setName("Turbulence Model Extrapolate Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _variable.deep_copy(1.5); - _grad_variable.deep_copy(2.0); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create extrapolate evaluator. - const std::string variable_name = "variable"; - const auto extr_eval = Teuchos::rcp( - new BoundaryCondition::TurbulenceExtrapolate( - *test_fixture.ir, variable_name)); - test_fixture.registerEvaluator(extr_eval); - - // Add required test fields. - test_fixture.registerTestField(extr_eval->_boundary_variable); - test_fixture.registerTestField( - extr_eval->_boundary_grad_variable); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_var_result = test_fixture.getTestFieldData( - extr_eval->_boundary_variable); - const auto boundary_grad_var_result - = test_fixture.getTestFieldData( - extr_eval->_boundary_grad_variable); - - const int num_point = boundary_var_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(1.5, fieldValue(boundary_var_result, 0, qp)); - - for (int d = 0; d < num_grad_dim; ++d) - { - EXPECT_DOUBLE_EQ(2.0, - fieldValue(boundary_grad_var_result, 0, qp, d)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D case -TEST(Test2DTurbulenceExtrapolate, residual) -{ - testEval(2); -} - -TEST(Test2DTurbulenceExtrapolate, jacobian) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D case -TEST(Test3DTurbulenceExtrapolate, residual) -{ - testEval(3); -} - -TEST(Test3DTurbulenceExtrapolate, jacobian) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceFixed.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceFixed.cpp deleted file mode 100644 index 823312f..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceFixed.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceFixed.hpp" -#include - -#include -#include -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField - _grad_variable; - - Dependencies(const panzer::IntegrationRule& ir) - : _grad_variable("GRAD_variable", ir.dl_vector) - { - this->addEvaluatedField(_grad_variable); - this->setName("Turbulence Model Fixed Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _grad_variable.deep_copy(2.0); - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create dirichlet evaluator. - const std::string variable_name = "variable"; - Teuchos::ParameterList bc_params; - bc_params.set("variable Value", 3.0); - const auto fixed_eval = Teuchos::rcp( - new BoundaryCondition::TurbulenceFixed( - *test_fixture.ir, bc_params, variable_name)); - test_fixture.registerEvaluator(fixed_eval); - - // Add required test fields. - test_fixture.registerTestField(fixed_eval->_boundary_variable); - test_fixture.registerTestField( - fixed_eval->_boundary_grad_variable); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_var_result = test_fixture.getTestFieldData( - fixed_eval->_boundary_variable); - const auto boundary_grad_var_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_variable); - - const int num_point = boundary_var_result.extent(1); - const double exp_value = 2.0; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(3.0, fieldValue(boundary_var_result, 0, qp)); - - for (int d = 0; d < num_grad_dim; ++d) - { - EXPECT_DOUBLE_EQ(exp_value, - fieldValue(boundary_grad_var_result, 0, qp, d)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D case -TEST(Test2D, residual) -{ - testEval(2); -} - -TEST(Test2D, jacobian) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D case -TEST(Test3D, residual) -{ - testEval(3); -} - -TEST(Test3D, jacobian) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceInletOutlet.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceInletOutlet.cpp deleted file mode 100644 index 9e41f5c..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceInletOutlet.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceInletOutlet.hpp" -#include - -#include - -#include -#include -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _u0, _u1, _u2; - - PHX::MDField _variable; - PHX::MDField - _grad_variable; - PHX::MDField _velocity_0; - PHX::MDField _velocity_1; - PHX::MDField _velocity_2; - PHX::MDField _normals; - - Dependencies(const panzer::IntegrationRule& ir, - const double u0, - const double u1, - const double u2) - : _u0(u0) - , _u1(u1) - , _u2(u2) - , _variable("variable", ir.dl_scalar) - , _grad_variable("GRAD_variable", ir.dl_vector) - , _velocity_0("velocity_0", ir.dl_scalar) - , _velocity_1("velocity_1", ir.dl_scalar) - , _velocity_2("velocity_2", ir.dl_scalar) - , _normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(_variable); - this->addEvaluatedField(_grad_variable); - this->addEvaluatedField(_velocity_0); - this->addEvaluatedField(_velocity_1); - this->addEvaluatedField(_velocity_2); - this->addEvaluatedField(_normals); - this->setName("Turbulence Model Inlet/Outlet Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _variable.deep_copy(2.0); - _grad_variable.deep_copy(3.0); - _normals.deep_copy(4.0); - _velocity_0.deep_copy(_u0); - _velocity_1.deep_copy(_u1); - _velocity_2.deep_copy(_u2); - } -}; - -//---------------------------------------------------------------------------// -// Inlet/outlet cases -enum class InletOutlet -{ - inlet, - outlet -}; - -//---------------------------------------------------------------------------// -template -void testEval(const InletOutlet inlet_outlet) -{ - // Test fixture - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Set sign for inlet or outlet flow - double sign = 0.0; - - switch (inlet_outlet) - { - case (InletOutlet::inlet): - sign = -1.0; - break; - case (InletOutlet::outlet): - sign = 1.0; - break; - } - - // Create dependencies - const double _nanval = std::numeric_limits::quiet_NaN(); - const double u0 = 5.0 * sign; - const double u1 = 6.0 * sign; - const double u2 = num_space_dim == 3 ? 7.0 * sign : _nanval; - - const auto dep_eval = Teuchos::rcp( - new Dependencies(*test_fixture.ir, u0, u1, u2)); - test_fixture.registerEvaluator(dep_eval); - - // Create inlet/outlet evaluator. - const std::string variable_name = "variable"; - Teuchos::ParameterList bc_params; - bc_params.set("variable Inlet Value", 2.75); - const auto fixed_eval = Teuchos::rcp( - new BoundaryCondition:: - TurbulenceInletOutlet( - *test_fixture.ir, bc_params, variable_name)); - test_fixture.registerEvaluator(fixed_eval); - - // Add required test fields. - test_fixture.registerTestField(fixed_eval->_boundary_variable); - test_fixture.registerTestField( - fixed_eval->_boundary_grad_variable); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_var_result = test_fixture.getTestFieldData( - fixed_eval->_boundary_variable); - const auto boundary_grad_var_result - = test_fixture.getTestFieldData( - fixed_eval->_boundary_grad_variable); - - const int num_point = boundary_var_result.extent(1); - const double exp_value = sign > 0 ? 2.0 : 2.75; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_value, fieldValue(boundary_var_result, 0, qp)); - - for (int d = 0; d < num_space_dim; ++d) - { - EXPECT_DOUBLE_EQ(3.0, - fieldValue(boundary_grad_var_result, 0, qp, d)); - } - } -} -//---------------------------------------------------------------------------// -// Value parameterized test fixture -struct EvaluationTest : public testing::TestWithParam -{ - // Case generator for parameterized test - struct ParamNameGenerator - { - std::string - operator()(const testing::TestParamInfo& info) const - { - const auto inlet_outlet = info.param; - switch (inlet_outlet) - { - case (InletOutlet::inlet): - return "inlet"; - case (InletOutlet::outlet): - return "outlet"; - default: - return "INVALID_NAME"; - } - } - }; -}; - -//---------------------------------------------------------------------------// -// Residual evaluation, 2D -TEST_P(EvaluationTest, residual2D) -{ - InletOutlet inlet_outlet; - inlet_outlet = GetParam(); - testEval(inlet_outlet); -} - -//---------------------------------------------------------------------------// -// Residual evaluation, 3D -TEST_P(EvaluationTest, residual3D) -{ - InletOutlet inlet_outlet; - inlet_outlet = GetParam(); - testEval(inlet_outlet); -} - -//---------------------------------------------------------------------------// -// Jacobian evaluation, 2D -TEST_P(EvaluationTest, jacobian2D) -{ - InletOutlet inlet_outlet; - inlet_outlet = GetParam(); - testEval(inlet_outlet); -} - -//---------------------------------------------------------------------------// -// Jacobian evaluation, 3D -TEST_P(EvaluationTest, jacobian3D) -{ - InletOutlet inlet_outlet; - inlet_outlet = GetParam(); - testEval(inlet_outlet); -} - -//---------------------------------------------------------------------------// -// Generate test suite with inlet and outlet conditions -INSTANTIATE_TEST_SUITE_P(InletOutletBC, - EvaluationTest, - testing::Values(InletOutlet::inlet, - InletOutlet::outlet), - EvaluationTest::ParamNameGenerator{}); - -//---------------------------------------------------------------------------// -} // end namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceKEpsilonWallFunction.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceKEpsilonWallFunction.cpp deleted file mode 100644 index 279ad62..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceKEpsilonWallFunction.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceKEpsilonWallFunction.hpp" - -#include -#include -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _k; - PHX::MDField _e; - PHX::MDField _vel_0; - PHX::MDField _vel_1; - PHX::MDField _vel_2; - - PHX::MDField _prod_k; - PHX::MDField _dest_k; - PHX::MDField _source_k; - - PHX::MDField _nu_t; - - PHX::MDField _grad_k; - PHX::MDField _grad_e; - - PHX::MDField _normals; - - const bool _low_k; - const double _nanval; - - Dependencies(const panzer::IntegrationRule& ir, const bool low_k) - : _k("turb_kinetic_energy", ir.dl_scalar) - , _e("turb_dissipation_rate", ir.dl_scalar) - , _vel_0("velocity_0", ir.dl_scalar) - , _vel_1("velocity_1", ir.dl_scalar) - , _vel_2("velocity_2", ir.dl_scalar) - , _prod_k("PRODUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _dest_k("DESTRUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _source_k("SOURCE_turb_kinetic_energy_equation", ir.dl_scalar) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , _grad_k("GRAD_turb_kinetic_energy", ir.dl_vector) - , _grad_e("GRAD_turb_dissipation_rate", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - , _low_k(low_k) - , _nanval(std::numeric_limits::quiet_NaN()) - { - this->addEvaluatedField(_k); - this->addEvaluatedField(_e); - this->addEvaluatedField(_vel_0); - this->addEvaluatedField(_vel_1); - this->addEvaluatedField(_vel_2); - this->addEvaluatedField(_prod_k); - this->addEvaluatedField(_dest_k); - this->addEvaluatedField(_source_k); - this->addEvaluatedField(_nu_t); - this->addEvaluatedField(_grad_k); - this->addEvaluatedField(_grad_e); - this->addEvaluatedField(_normals); - this->setName( - "Turbulence Model K-Epsilon Wall Function Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "turbulence k-epsilon wall function test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - using std::pow; - - const int num_point = _k.extent(1); - const int num_space_dim = _normals.extent(2); - - for (int qp = 0; qp < num_point; ++qp) - { - _k(c, qp) = _low_k ? 0.25 : 2.5; - _e(c, qp) = 3.1; - _vel_0(c, qp) = 1.5; - _vel_1(c, qp) = -3.0; - _vel_2(c, qp) = num_space_dim == 3 ? 4.5 : _nanval; - - _prod_k(c, qp) = 7.5; - _dest_k(c, qp) = -2.2; - _source_k(c, qp) = 5.3; - - _nu_t(c, qp) = 4.8; - - for (int d = 0; d < num_space_dim; ++d) - { - _grad_k(c, qp, d) = 0.02 * (d + 1.0); - _grad_e(c, qp, d) = 0.03 * (d + 1.0); - _normals(c, qp, d) = 0.33 * (d + 1.0); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const bool low_k, const bool neumann) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - constexpr int num_space_dim = NumSpaceDim; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - // Create fluid properties - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.0001); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Create boundary parameters - Teuchos::ParameterList bc_params; - bc_params.set("Epsilon Condition Type", neumann ? "Neumann" : "Dirichlet"); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir, low_k)); - test_fixture.registerEvaluator(dep_eval); - - // Create wall function evaluator - const auto wall_eval = Teuchos::rcp( - new BoundaryCondition::TurbulenceKEpsilonWallFunction( - *test_fixture.ir, bc_params, fluid_prop)); - test_fixture.registerEvaluator(wall_eval); - - // Add required test fields. - test_fixture.registerTestField(wall_eval->_boundary_k); - test_fixture.registerTestField(wall_eval->_boundary_e); - test_fixture.registerTestField(wall_eval->_boundary_grad_k); - test_fixture.registerTestField(wall_eval->_boundary_grad_e); - test_fixture.registerTestField(wall_eval->_boundary_u_tau); - test_fixture.registerTestField(wall_eval->_boundary_y_plus); - test_fixture.registerTestField(wall_eval->_wall_func_nu_t); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_k_result - = test_fixture.getTestFieldData(wall_eval->_boundary_k); - const auto boundary_e_result - = test_fixture.getTestFieldData(wall_eval->_boundary_e); - const auto boundary_grad_k_result - = test_fixture.getTestFieldData(wall_eval->_boundary_grad_k); - const auto boundary_grad_e_result - = test_fixture.getTestFieldData(wall_eval->_boundary_grad_e); - const auto boundary_u_tau_result - = test_fixture.getTestFieldData(wall_eval->_boundary_u_tau); - const auto boundary_y_plus_result - = test_fixture.getTestFieldData(wall_eval->_boundary_y_plus); - const auto wall_func_nu_t_result - = test_fixture.getTestFieldData(wall_eval->_wall_func_nu_t); - - // Set expected values (see doc script) - const double exp_k = low_k ? 0.25 : 2.5; - - const double nan_val = std::numeric_limits::quiet_NaN(); - - const double exp_grad_k_2D[3] = {0.00911, 0.01822, nan_val}; - const double exp_grad_k_3D[3] = {-0.010492, -0.020984, -0.031476}; - const auto exp_grad_k = (num_space_dim == 3) ? exp_grad_k_3D - : exp_grad_k_2D; - - // Set expected epsilon boundary value for Dirichlet condition (see doc - // file) - const double exp_e_dir_2D = low_k ? 18.65286527037969 : 1240.4622237904111; - const double exp_e_dir_3D = low_k ? 146.23846371977686 : 1240.4622237904111; - const double exp_e_dir = num_space_dim == 3 ? exp_e_dir_3D : exp_e_dir_2D; - - // Expected epsilon boundary value for Neumann condition is interior value - const double exp_e_neu = 3.1; - - // Set expected boundary epsilon based on condition type - const double exp_e = neumann ? exp_e_neu : exp_e_dir; - - // Expected boundary epsilon gradient equal to interior gradient for - // Dirichlet condition - const double exp_grad_e_dir[3] - = {0.03, 0.06, num_space_dim == 3 ? 0.09 : nan_val}; - - // Expected boundary gradient calculated for Neumann condition (see doc - // file) - const double exp_grad_e_2D[3] - = {320533.19035428844, 641066.3807085769, nan_val}; - const double exp_grad_e_2D_low_k[3] - = {1687.8312232028663, 3375.6624464057327, nan_val}; - const double exp_grad_e_3D[3] - = {320533.1609512885, 641066.321902577, 961599.4828538651}; - const double exp_grad_e_3D_low_k[3] - = {22142.174555921218, 44284.349111842435, 66426.52366776364}; - const auto exp_grad_e_neu - = (num_space_dim == 3) ? (low_k ? exp_grad_e_3D_low_k : exp_grad_e_3D) - : (low_k ? exp_grad_e_2D_low_k : exp_grad_e_2D); - - // Set expected epsilon values based on condition type - const auto exp_grad_e = neumann ? exp_grad_e_neu : exp_grad_e_dir; - - // Set other boundary turbulence quantities - const auto exp_u_tau_2D = low_k ? 0.3032641922468069 : 0.8660254037844386; - const auto exp_u_tau_3D = low_k ? 0.507458054264097 : 0.8660254037844386; - const auto exp_u_tau = num_space_dim == 3 ? exp_u_tau_3D : exp_u_tau_2D; - const auto exp_y_plus = 11.06; - const auto neu_nu_t = 0.00045346000000000004; - const auto dir_nu_t = low_k ? 0.001814516129032258 : 0.1814516129032258; - const auto exp_wall_func_nu_t = neumann ? neu_nu_t : dir_nu_t; - - // Check boundary values - const int num_point = boundary_k_result.extent(1); - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_k, fieldValue(boundary_k_result, 0, qp)); - EXPECT_DOUBLE_EQ(exp_e, fieldValue(boundary_e_result, 0, qp)); - - for (int d = 0; d < num_space_dim; ++d) - { - EXPECT_DOUBLE_EQ(exp_grad_k[d], - fieldValue(boundary_grad_k_result, 0, qp, d)); - EXPECT_DOUBLE_EQ(exp_grad_e[d], - fieldValue(boundary_grad_e_result, 0, qp, d)); - } - - EXPECT_DOUBLE_EQ(exp_u_tau, fieldValue(boundary_u_tau_result, 0, qp)); - EXPECT_DOUBLE_EQ(exp_y_plus, fieldValue(boundary_y_plus_result, 0, qp)); - EXPECT_DOUBLE_EQ(exp_wall_func_nu_t, - fieldValue(wall_func_nu_t_result, 0, qp)); - } -} - -//---------------------------------------------------------------------------// -// 2-D case -TEST(Test2DTurbulenceKEpsilonWallFunctionDirichlet, residual) -{ - testEval(false, false); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionDirichlet, jacobian) -{ - testEval(false, false); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionLowKDirichlet, residual) -{ - testEval(true, false); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionLowKDirichlet, jacobian) -{ - testEval(true, false); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionNeumann, residual) -{ - testEval(false, true); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionNeumann, jacobian) -{ - testEval(false, true); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionLowKNeumann, residual) -{ - testEval(true, true); -} - -TEST(Test2DTurbulenceKEpsilonWallFunctionLowKNeumann, jacobian) -{ - testEval(true, true); -} - -//---------------------------------------------------------------------------// -// 3-D case -TEST(Test3DTurbulenceKEpsilonWallFunctionDirichlet, residual) -{ - testEval(false, false); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionDirichlet, jacobian) -{ - testEval(false, false); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionLowKDirichlet, residual) -{ - testEval(true, false); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionLowKDirichlet, jacobian) -{ - testEval(true, false); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionNeumann, residual) -{ - testEval(false, true); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionNeumann, jacobian) -{ - testEval(false, true); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionLowKNeumann, residual) -{ - testEval(true, true); -} - -TEST(Test3DTurbulenceKEpsilonWallFunctionLowKNeumann, jacobian) -{ - testEval(true, true); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceSymmetry.cpp b/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceSymmetry.cpp deleted file mode 100644 index 47973d2..0000000 --- a/src/turbulence_models/boundary_conditions/unit_test/tstTurbulenceSymmetry.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "turbulence_models/boundary_conditions/VertexCFD_BoundaryState_TurbulenceSymmetry.hpp" -#include - -#include -#include -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -// Test data dependencies. -template -struct Dependencies : public PHX::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField _variable; - - PHX::MDField - _grad_variable; - - PHX::MDField _normals; - - Dependencies(const panzer::IntegrationRule& ir) - : _variable("variable", ir.dl_scalar) - , _grad_variable("GRAD_variable", ir.dl_vector) - , _normals("Side Normal", ir.dl_vector) - { - this->addEvaluatedField(_variable); - this->addEvaluatedField(_grad_variable); - this->addEvaluatedField(_normals); - this->setName("Turbulence Model Symmetry Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "turbulence symmetry test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - using std::sqrt; - - const int num_point = _variable.extent(1); - const int num_space_dim = _normals.extent(2); - - for (int qp = 0; qp < num_point; ++qp) - { - _variable(c, qp) = 2.0; - - for (int d = 0; d < num_space_dim; ++d) - { - _grad_variable(c, qp, d) = 0.02 * (d + 1.0); - _normals(c, qp, d) = 0.33 * (d + 1.0); - } - } - } -}; - -//---------------------------------------------------------------------------// -template -void testEval(const int num_grad_dim) -{ - // Test fixture - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_grad_dim, integration_order, basis_order); - - // Create dependencies - const auto dep_eval - = Teuchos::rcp(new Dependencies(*test_fixture.ir)); - test_fixture.registerEvaluator(dep_eval); - - // Create symmetry evaluator. - const std::string variable_name = "variable"; - const auto symm_eval = Teuchos::rcp( - new BoundaryCondition::TurbulenceSymmetry( - *test_fixture.ir, variable_name)); - test_fixture.registerEvaluator(symm_eval); - - // Add required test fields. - test_fixture.registerTestField(symm_eval->_boundary_variable); - test_fixture.registerTestField( - symm_eval->_boundary_grad_variable); - - // Evaluate values - test_fixture.evaluate(); - - // Check values - const auto boundary_var_result = test_fixture.getTestFieldData( - symm_eval->_boundary_variable); - const auto boundary_grad_var_result - = test_fixture.getTestFieldData( - symm_eval->_boundary_grad_variable); - - const int num_point = boundary_var_result.extent(1); - - const double nan_val = std::numeric_limits::quiet_NaN(); - - const double exp_grad_2D[3] = {0.00911, 0.01822, nan_val}; - const double exp_grad_3D[3] = { - -0.010492000000000001, -0.020984000000000003, -0.031476000000000004}; - const auto exp_grad_value = (num_grad_dim == 3) ? exp_grad_3D : exp_grad_2D; - - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(2.0, fieldValue(boundary_var_result, 0, qp)); - - for (int d = 0; d < num_grad_dim; ++d) - { - EXPECT_DOUBLE_EQ(exp_grad_value[d], - fieldValue(boundary_grad_var_result, 0, qp, d)); - } - } -} - -//---------------------------------------------------------------------------// -// 2-D case -TEST(Test2DTurbulenceSymmetry, residual) -{ - testEval(2); -} - -TEST(Test2DTurbulenceSymmetry, jacobian) -{ - testEval(2); -} - -//---------------------------------------------------------------------------// -// 3-D case -TEST(Test3DTurbulenceSymmetry, residual) -{ - testEval(3); -} - -TEST(Test3DTurbulenceSymmetry, jacobian) -{ - testEval(3); -} - -//---------------------------------------------------------------------------// -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.cpp deleted file mode 100644 index d5b8d11..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp" -#include "VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleKEpsilonDiffusivityCoefficient) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp deleted file mode 100644 index 5deebf3..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Diffusion coefficients for standard K-Epsilon turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleKEpsilonDiffusivityCoefficient - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleKEpsilonDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const double sigma_k = 1.0, - const double sigma_e = 1.3, - const std::string field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _nu_t; - - double _nu; - double _sigma_k; - double _sigma_e; - int _num_grad_dim; - - public: - PHX::MDField _diffusivity_var_k; - PHX::MDField _diffusivity_var_e; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient_impl.hpp deleted file mode 100644 index 9cc47b2..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient_impl.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKEpsilonDiffusivityCoefficient:: - IncompressibleKEpsilonDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const double sigma_k, - const double sigma_e, - const std::string field_prefix) - : _nu_t(field_prefix + "turbulent_eddy_viscosity", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _sigma_k(sigma_k) - , _sigma_e(sigma_e) - , _num_grad_dim(ir.spatial_dimension) - , _diffusivity_var_k("diffusivity_turb_kinetic_energy", ir.dl_scalar) - , _diffusivity_var_e("diffusivity_turb_dissipation_rate", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_nu_t); - - // Add evaluated fields - this->addEvaluatedField(_diffusivity_var_k); - this->addEvaluatedField(_diffusivity_var_e); - - // Closure model name - this->setName("K-Epsilon Incompressible Diffusivity Coefficient " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonDiffusivityCoefficient::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonDiffusivityCoefficient::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _diffusivity_var_k.extent(1); - - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, num_point), - [&](const int point) { - _diffusivity_var_k(cell, point) - = (_nu + _nu_t(cell, point) / _sigma_k); - _diffusivity_var_e(cell, point) - = (_nu + _nu_t(cell, point) / _sigma_e); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.cpp deleted file mode 100644 index 654048c..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp" -#include "VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleKEpsilonEddyViscosity) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp deleted file mode 100644 index 136b50f..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Turbulent eddy viscosity for standard K-Epsilon turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleKEpsilonEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleKEpsilonEddyViscosity(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_dissipation_rate; - - double _C_nu; - int _num_grad_dim; - - public: - PHX::MDField _nu_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity_impl.hpp deleted file mode 100644 index aa87c29..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity_impl.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKEpsilonEddyViscosity:: - IncompressibleKEpsilonEddyViscosity(const panzer::IntegrationRule& ir) - : _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - , _C_nu(0.09) - , _num_grad_dim(ir.spatial_dimension) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_dissipation_rate); - - // Add evaluated fields - this->addEvaluatedField(_nu_t); - - // Closure model name - this->setName("K-Epsilon Incompressible Turbulent Eddy Viscosity " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonEddyViscosity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonEddyViscosity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const auto max_tol = 1.0e-10; - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - _nu_t(cell, point) - = _C_nu * pow(_turb_kinetic_energy(cell, point), 2.0) - / SmoothMath::max( - _turb_dissipation_rate(cell, point), max_tol, 0.0); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.cpp deleted file mode 100644 index a195156..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKEpsilonSource.hpp" -#include "VertexCFD_Closure_IncompressibleKEpsilonSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleKEpsilonSource) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp deleted file mode 100644 index 9a6a5ce..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Source term for standard K-Epsilon turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleKEpsilonSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleKEpsilonSource(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _nu_t; - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_dissipation_rate; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _C_1; - double _C_2; - - public: - PHX::MDField _k_source; - PHX::MDField _k_prod; - PHX::MDField _k_dest; - PHX::MDField _e_source; - PHX::MDField _e_prod; - PHX::MDField _e_dest; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource_impl.hpp deleted file mode 100644 index 5ebefc9..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource_impl.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKEpsilonSource:: - IncompressibleKEpsilonSource(const panzer::IntegrationRule& ir) - : _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - , _C_1(1.44) - , _C_2(1.92) - , _k_source("SOURCE_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_prod("PRODUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_dest("DESTRUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _e_source("SOURCE_turb_dissipation_rate_equation", ir.dl_scalar) - , _e_prod("PRODUCTION_turb_dissipation_rate_equation", ir.dl_scalar) - , _e_dest("DESTRUCTION_turb_dissipation_rate_equation", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_nu_t); - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_dissipation_rate); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_k_source); - this->addEvaluatedField(_k_prod); - this->addEvaluatedField(_k_dest); - this->addEvaluatedField(_e_source); - this->addEvaluatedField(_e_prod); - this->addEvaluatedField(_e_dest); - - // Closure model name - this->setName("K-Epsilon Incompressible Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKEpsilonSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const double max_tol = 1.0e-10; - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Production term - _k_prod(cell, point) = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - _k_prod(cell, point) - += pow(0.5 - * (_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i)), - 2.0); - } - } - - // Turbulent kinetic energy terms - _k_prod(cell, point) *= (2.0 * _nu_t(cell, point)); - _k_dest(cell, point) = -_turb_dissipation_rate(cell, point); - _k_source(cell, point) = _k_prod(cell, point) - + _k_dest(cell, point); - - // Turbulent dissipation rate terms - _e_prod(cell, point) - = _C_1 * _turb_dissipation_rate(cell, point) - / SmoothMath::max( - _turb_kinetic_energy(cell, point), max_tol, 0.0) - * _k_prod(cell, point); - _e_dest(cell, point) - = -_C_2 * pow(_turb_dissipation_rate(cell, point), 2.0) - / SmoothMath::max( - _turb_kinetic_energy(cell, point), max_tol, 0.0); - _e_source(cell, point) = _e_prod(cell, point) - + _e_dest(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONSOURCE_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.cpp deleted file mode 100644 index b0aa2a7..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp" -#include "VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleKOmegaDiffusivityCoefficient) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp deleted file mode 100644 index f1bae19..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGADIFFUSIVITYCOEFFICIENT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGADIFFUSIVITYCOEFFICIENT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Diffusivity coefficients for Wilcox (2006) K-Omega turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleKOmegaDiffusivityCoefficient - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleKOmegaDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_specific_dissipation_rate; - - double _nu; - double _sigma_k; - double _sigma_w; - - public: - PHX::MDField _diffusivity_var_k; - PHX::MDField _diffusivity_var_w; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGADIFFUSIVITYCOEFFICIENT_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient_impl.hpp deleted file mode 100644 index 276bae1..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient_impl.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKEPSILONDIFFUSIVITYCOEFFICIENT_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKOmegaDiffusivityCoefficient:: - IncompressibleKOmegaDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop, - const Teuchos::ParameterList& user_params) - : _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _sigma_k(0.6) - , _sigma_w(0.5) - , _diffusivity_var_k("diffusivity_turb_kinetic_energy", ir.dl_scalar) - , _diffusivity_var_w("diffusivity_turb_specific_dissipation_rate", - ir.dl_scalar) -{ - // Check for user-defined coefficients or parameters - if (user_params.isSublist("Turbulence Parameters")) - { - Teuchos::ParameterList turb_list - = user_params.sublist("Turbulence Parameters"); - - if (turb_list.isSublist("K-Omega Parameters")) - { - Teuchos::ParameterList k_w_list - = turb_list.sublist("K-Omega Parameters"); - - if (k_w_list.isType("sigma_w")) - { - _sigma_w = k_w_list.get("sigma_w"); - } - - if (k_w_list.isType("sigma_k")) - { - _sigma_k = k_w_list.get("sigma_k"); - } - } - } - - // Add dependent fields - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_specific_dissipation_rate); - - // Add evaluated fields - this->addEvaluatedField(_diffusivity_var_k); - this->addEvaluatedField(_diffusivity_var_w); - - // Closure model name - this->setName("K-Omega Incompressible Diffusivity Coefficient " - + std::to_string(ir.spatial_dimension) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaDiffusivityCoefficient::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaDiffusivityCoefficient::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _diffusivity_var_k.extent(1); - const double k_tol = 1e-20; - const double w_tol = 1e-10; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - const scalar_type turb_ratio - = SmoothMath::max(_turb_kinetic_energy(cell, point), k_tol, 0.0) - / SmoothMath::max( - _turb_specific_dissipation_rate(cell, point), w_tol, 0.0); - _diffusivity_var_k(cell, point) = _nu + _sigma_k * turb_ratio; - _diffusivity_var_w(cell, point) = _nu + _sigma_w * turb_ratio; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGADIFFUSIVITYCOEFFICIENT_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.cpp deleted file mode 100644 index 1edba0f..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp" -#include "VertexCFD_Closure_IncompressibleKOmegaEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleKOmegaEddyViscosity) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp deleted file mode 100644 index a825327..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Turbulent eddy viscosity for the Wilcox (2006) K-Omega turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleKOmegaEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleKOmegaEddyViscosity(const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_specific_dissipation_rate; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _C_lim; - double _beta_star; - - public: - PHX::MDField _nu_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity_impl.hpp deleted file mode 100644 index 4de31c2..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity_impl.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKOmegaEddyViscosity:: - IncompressibleKOmegaEddyViscosity(const panzer::IntegrationRule& ir) - : _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - , _C_lim(7.0 / 8.0) - , _beta_star(0.09) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_specific_dissipation_rate); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_nu_t); - - // Closure model name - this->setName("Incompressible K-Omega Turbulent Eddy Viscosity " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaEddyViscosity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaEddyViscosity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - using std::pow; - using std::sqrt; - - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const double max_tol = 1.0e-10; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - scalar_type Sij_Sij = 0.0; - for (int i = 0; i < num_space_dim; ++i) - { - Sij_Sij += pow(_grad_velocity[i](cell, point, i), 2.0); - for (int j = i + 1; j < num_space_dim; ++j) - { - Sij_Sij += 0.5 - * pow(_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i), - 2.0); - } - } - _nu_t(cell, point) - = _turb_kinetic_energy(cell, point) - / SmoothMath::max( - SmoothMath::max( - _turb_specific_dissipation_rate(cell, point), - _C_lim * sqrt(2.0 * Sij_Sij / _beta_star), - 0.0), - max_tol, - 0.0); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGAEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.cpp deleted file mode 100644 index 9fd109d..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleKOmegaSource.hpp" -#include "VertexCFD_Closure_IncompressibleKOmegaSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleKOmegaSource) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp deleted file mode 100644 index c3a1bd1..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Source term for the Wilcox (2006) K-Omega turbulence model with/without -// limiter -//---------------------------------------------------------------------------// -template -class IncompressibleKOmegaSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleKOmegaSource(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _nu_t; - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_specific_dissipation_rate; - - PHX::MDField - _grad_turb_kinetic_energy; - PHX::MDField - _grad_turb_specific_dissipation_rate; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _beta_star; - double _gamma; - double _beta_0; - double _sigma_d; - bool _limit_production; - - public: - PHX::MDField _k_source; - PHX::MDField _k_prod; - PHX::MDField _k_dest; - PHX::MDField _w_source; - PHX::MDField _w_prod; - PHX::MDField _w_dest; - PHX::MDField _w_cross; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource_impl.hpp deleted file mode 100644 index 566a8f5..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource_impl.hpp +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleKOmegaSource::IncompressibleKOmegaSource( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params) - : _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - , - - _grad_turb_kinetic_energy("GRAD_turb_kinetic_energy", ir.dl_vector) - , _grad_turb_specific_dissipation_rate( - "GRAD_turb_specific_dissipation_rate", ir.dl_vector) - , _beta_star(0.09) - , _gamma(0.52) - , _beta_0(0.0708) - , _sigma_d(0.125) - , _limit_production(true) - , _k_source("SOURCE_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_prod("PRODUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_dest("DESTRUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _w_source("SOURCE_turb_specific_dissipation_rate_equation", ir.dl_scalar) - , _w_prod("PRODUCTION_turb_specific_dissipation_rate_equation", - ir.dl_scalar) - , _w_dest("DESTRUCTION_turb_specific_dissipation_rate_equation", - ir.dl_scalar) - , _w_cross("CROSS_DIFFUSION_turb_specific_dissipation_rate_equation", - ir.dl_scalar) -{ - // Check for user-defined coefficients or parameters - if (user_params.isSublist("Turbulence Parameters")) - { - Teuchos::ParameterList turb_list - = user_params.sublist("Turbulence Parameters"); - - if (turb_list.isType("beta_star")) - { - _beta_star = turb_list.get("beta_star"); - } - - if (turb_list.isType("gamma")) - { - _gamma = turb_list.get("gamma"); - } - - if (turb_list.isType("beta_0")) - { - _beta_0 = turb_list.get("beta_0"); - } - - if (turb_list.isType("sigma_d")) - { - _sigma_d = turb_list.get("sigma_d"); - } - - if (turb_list.isType("Limit Production Term")) - { - _limit_production = turb_list.get("Limit Production Term"); - } - } - - // Add dependent fields - this->addDependentField(_nu_t); - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_specific_dissipation_rate); - this->addDependentField(_grad_turb_kinetic_energy); - this->addDependentField(_grad_turb_specific_dissipation_rate); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_k_source); - this->addEvaluatedField(_k_prod); - this->addEvaluatedField(_k_dest); - this->addEvaluatedField(_w_source); - this->addEvaluatedField(_w_prod); - this->addEvaluatedField(_w_dest); - this->addEvaluatedField(_w_cross); - - // Closure model name - this->setName("K-Omega Incompressible Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaSource::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleKOmegaSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const double max_tol = 1.0e-10; - using std::abs; - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - scalar_type Sij_Sij = 0.0; - for (int i = 0; i < num_space_dim; ++i) - { - Sij_Sij += pow(_grad_velocity[i](cell, point, i), 2.0); - for (int j = i + 1; j < num_space_dim; ++j) - { - Sij_Sij += 0.5 - * pow(_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i), - 2.0); - } - } - - scalar_type chi_w = 0.0; - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - for (int k = 0; k < num_space_dim; ++k) - { - chi_w += 0.125 - * (_grad_velocity[i](cell, point, j) - - _grad_velocity[j](cell, point, i)) - * (_grad_velocity[j](cell, point, k) - - _grad_velocity[k](cell, point, j)) - * (_grad_velocity[i](cell, point, k) - + _grad_velocity[k](cell, point, i)); - } - } - } - - chi_w = abs( - chi_w - / SmoothMath::max( - pow(_beta_star - * _turb_specific_dissipation_rate(cell, point), - 3.0), - max_tol, - 0.0)); - - const scalar_type f_beta = (1.0 + 85.0 * chi_w) - / (1.0 + 100.0 * chi_w); - - // Turbulent kinetic energy terms - _k_prod(cell, point) = _nu_t(cell, point) * Sij_Sij; - - _k_dest(cell, point) - = -_beta_star * _turb_specific_dissipation_rate(cell, point) - * _turb_kinetic_energy(cell, point); - - if (_limit_production) - { - // The limiter term is -20 times the k destruction term - _k_prod(cell, point) = SmoothMath::min( - _k_prod(cell, point), -20.0 * _k_dest(cell, point), 0.0); - } - - _k_source(cell, point) = _k_prod(cell, point) - + _k_dest(cell, point); - - // Turbulent dissipation rate terms - _w_prod(cell, point) - = _gamma * _turb_specific_dissipation_rate(cell, point) - * _nu_t(cell, point) * Sij_Sij - / SmoothMath::max( - _turb_kinetic_energy(cell, point), max_tol, 0.0); - _w_dest(cell, point) - = -_beta_0 * f_beta - * pow(_turb_specific_dissipation_rate(cell, point), 2); - - // Compute the cross diffusion term - _w_cross(cell, point) = 0.0; - scalar_type dkdxj_dwdxj = 0.0; - for (int j = 0; j < num_space_dim; ++j) - { - dkdxj_dwdxj - += _grad_turb_kinetic_energy(cell, point, j) - * _grad_turb_specific_dissipation_rate(cell, point, j); - } - if (dkdxj_dwdxj > 0.0) - { - _w_cross(cell, point) - = _sigma_d * dkdxj_dwdxj - / SmoothMath::max( - _turb_specific_dissipation_rate(cell, point), - max_tol, - 0.0); - } - - _w_source(cell, point) = _w_prod(cell, point) + _w_dest(cell, point) - + _w_cross(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEKOMEGASOURCE_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.cpp deleted file mode 100644 index 451fa3c..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp" -#include "VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleRealizableKEpsilonEddyViscosity) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp deleted file mode 100644 index 82a677a..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Turbulent eddy viscosity for Realizable K-Epsilon turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleRealizableKEpsilonEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleRealizableKEpsilonEddyViscosity( - const panzer::IntegrationRule& ir); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_dissipation_rate; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _A_0; - - public: - PHX::MDField _nu_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity_impl.hpp deleted file mode 100644 index 2e5aabf..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity_impl.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleRealizableKEpsilonEddyViscosity:: - IncompressibleRealizableKEpsilonEddyViscosity( - const panzer::IntegrationRule& ir) - : _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - , _A_0(4.0) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_dissipation_rate); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_nu_t); - - // Closure model name - this->setName( - "Realizable K-Epsilon Incompressible Turbulent Eddy Viscosity " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRealizableKEpsilonEddyViscosity:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRealizableKEpsilonEddyViscosity:: -operator()(const Kokkos::TeamPolicy::member_type& team) const -{ - using std::acos; - using std::cos; - using std::pow; - using std::sqrt; - - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const auto tol = 1.0e-10; - const double sqrt_six = sqrt(6.0); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Calculate mag square of symmetric and skew symmetric velocity - // gradient components. - // COMMENT: The Omega2 term must be modified if these equations - // are to be solved in a rotating reference frame. - scalar_type S2 = 0.0; - scalar_type Omega2 = 0.0; - scalar_type W = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - S2 += pow(0.5 - * (_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i)), - 2.0); - Omega2 += pow(0.5 - * (_grad_velocity[i](cell, point, j) - - _grad_velocity[j](cell, point, i)), - 2.0); - for (int k = 0; k < num_space_dim; ++k) - { - W += 1.0 / 8.0 - * (_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i)) - * (_grad_velocity[j](cell, point, k) - + _grad_velocity[k](cell, point, j)) - * (_grad_velocity[k](cell, point, i) - + _grad_velocity[i](cell, point, k)); - } - } - } - - S2 = SmoothMath::max(S2, tol, 0.0); - Omega2 = SmoothMath::max(Omega2, tol, 0.0); - W /= pow(S2, 1.5); - - // Calculate parameters for viscosity - const scalar_type Us = sqrt(S2 + Omega2); - const scalar_type phi - = 1.0 / 3.0 - * acos(SmoothMath::max( - SmoothMath::min(sqrt_six * W, 1.0, 0.0), -1.0, 0.0)); - const scalar_type As = sqrt_six * cos(phi); - const scalar_type C_nu - = 1.0 - / (_A_0 - + (As * Us * _turb_kinetic_energy(cell, point) - / SmoothMath::max( - _turb_dissipation_rate(cell, point), tol, 0.0))); - - // Eddy viscosity calculation - _nu_t(cell, point) - = C_nu * pow(_turb_kinetic_energy(cell, point), 2.0) - / SmoothMath::max( - _turb_dissipation_rate(cell, point), tol, 0.0); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.cpp deleted file mode 100644 index bc6ee70..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp" -#include "VertexCFD_Closure_IncompressibleRealizableKEpsilonSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleRealizableKEpsilonSource) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp deleted file mode 100644 index ef5f2d7..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Source term for realizable K-Epsilon turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleRealizableKEpsilonSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleRealizableKEpsilonSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _nu_t; - PHX::MDField - _turb_kinetic_energy; - PHX::MDField - _turb_dissipation_rate; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _nu; - double _C_2; - - public: - PHX::MDField _k_source; - PHX::MDField _k_prod; - PHX::MDField _k_dest; - PHX::MDField _e_source; - PHX::MDField _e_prod; - PHX::MDField _e_dest; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource_impl.hpp deleted file mode 100644 index b1d1188..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource_impl.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleRealizableKEpsilonSource:: - IncompressibleRealizableKEpsilonSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _C_2(1.9) - , _k_source("SOURCE_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_prod("PRODUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _k_dest("DESTRUCTION_turb_kinetic_energy_equation", ir.dl_scalar) - , _e_source("SOURCE_turb_dissipation_rate_equation", ir.dl_scalar) - , _e_prod("PRODUCTION_turb_dissipation_rate_equation", ir.dl_scalar) - , _e_dest("DESTRUCTION_turb_dissipation_rate_equation", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_nu_t); - this->addDependentField(_turb_kinetic_energy); - this->addDependentField(_turb_dissipation_rate); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_k_source); - this->addEvaluatedField(_k_prod); - this->addEvaluatedField(_k_dest); - this->addEvaluatedField(_e_source); - this->addEvaluatedField(_e_prod); - this->addEvaluatedField(_e_dest); - - // Closure model name - this->setName("Realizable K-Epsilon Incompressible Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRealizableKEpsilonSource:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleRealizableKEpsilonSource:: -operator()(const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const double max_tol = 1.0e-10; - using std::pow; - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Calculate S and mag square of velocity gradient - scalar_type S2 = 0.0; - scalar_type grad_u_sqr = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - S2 += pow(0.5 - * (_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i)), - 2.0); - grad_u_sqr += pow(_grad_velocity[i](cell, point, j), 2.0); - } - } - - const scalar_type S = sqrt(SmoothMath::max(2.0 * S2, max_tol, 0.0)); - - // Calculate model coefficients - const scalar_type eta - = S * _turb_kinetic_energy(cell, point) - / SmoothMath::max( - _turb_dissipation_rate(cell, point), max_tol, 0.0); - const scalar_type C_1 - = SmoothMath::max(0.43, eta / (5.0 + eta), 0.0); - - // Turbulent kinetic energy terms (same as standard K-Epsilon) - _k_prod(cell, point) = _nu_t(cell, point) * grad_u_sqr; - _k_dest(cell, point) = -_turb_dissipation_rate(cell, point); - _k_source(cell, point) = _k_prod(cell, point) - + _k_dest(cell, point); - - // Turbulent dissipation rate terms (modified for RKE model) - _e_prod(cell, point) = C_1 * S - * _turb_dissipation_rate(cell, point); - _e_dest(cell, point) - = -_C_2 * pow(_turb_dissipation_rate(cell, point), 2.0) - / SmoothMath::max( - _turb_kinetic_energy(cell, point) - + sqrt(SmoothMath::max( - _nu * _turb_dissipation_rate(cell, point), - max_tol, - 0.0)), - max_tol, - 0.0); - _e_source(cell, point) = _e_prod(cell, point) - + _e_dest(cell, point); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEREALIZABLEKEPSILONSOURCE_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.cpp deleted file mode 100644 index 6d524b3..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp" -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleSpalartAllmarasDiffusivityCoefficient) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp deleted file mode 100644 index f0ad41b..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Diffusion coefficient for Spalart-Allmaras turbulence model (SA-neg) -//---------------------------------------------------------------------------// -template -class IncompressibleSpalartAllmarasDiffusivityCoefficient - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleSpalartAllmarasDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _sa_var; - - const double _nu; - const double _cn1; - const double _sigma; - const scalar_type _one; - const int _num_grad_dim; - - public: - PHX::MDField _diffusivity_var; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient_impl.hpp deleted file mode 100644 index 9170fe1..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient_impl.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_IMPL_HPP - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleSpalartAllmarasDiffusivityCoefficient:: - IncompressibleSpalartAllmarasDiffusivityCoefficient( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _sa_var("spalart_allmaras_variable", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _cn1(16.0) - , _sigma(2.0 / 3.0) - , _one(1.0) - , _num_grad_dim(ir.spatial_dimension) - , _diffusivity_var("diffusivity_spalart_allmaras_variable", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_sa_var); - - // Add evaluated fields - this->addEvaluatedField(_diffusivity_var); - - // Closure model name - this->setName("Spalart-Allmaras Incompressible Diffusivity Coefficient " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasDiffusivityCoefficient:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasDiffusivityCoefficient:: -operator()(const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _diffusivity_var.extent(1); - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - const scalar_type xi3 = pow(_sa_var(cell, point) / _nu, 3.0); - const scalar_type f_n = _sa_var(cell, point) < 0.0 - ? (_cn1 + xi3) / (_cn1 - xi3) - : _one; - - _diffusivity_var(cell, point) = (_nu + _sa_var(cell, point) * f_n) - / _sigma; - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASDIFFUSIVITYCOEFFICIENT_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.cpp deleted file mode 100644 index 79c1a7b..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp" -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS( - VertexCFD::ClosureModel::IncompressibleSpalartAllmarasEddyViscosity) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp deleted file mode 100644 index d3e64e6..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Turbulent eddy viscosity for Spalart-Allmaras turbulence model (SA-neg) -//---------------------------------------------------------------------------// -template -class IncompressibleSpalartAllmarasEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - IncompressibleSpalartAllmarasEddyViscosity( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _sa_var; - - const double _nu; - const double _cv1; - const int _num_grad_dim; - - public: - PHX::MDField _nu_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity_impl.hpp deleted file mode 100644 index 36242f4..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity_impl.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleSpalartAllmarasEddyViscosity:: - IncompressibleSpalartAllmarasEddyViscosity( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _sa_var("spalart_allmaras_variable", ir.dl_scalar) - , _nu(fluid_prop.constantKinematicViscosity()) - , _cv1(7.1) - , _num_grad_dim(ir.spatial_dimension) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_sa_var); - - // Add evaluated fields - this->addEvaluatedField(_nu_t); - - // Closure model name - this->setName("Spalart-Allmaras Incompressible Turbulent Eddy Viscosity " - + std::to_string(_num_grad_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasEddyViscosity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasEddyViscosity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - using std::pow; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - const auto sa_var = _sa_var(cell, point); - if (sa_var >= 0.0) - { - const scalar_type xi3 = pow(sa_var / _nu, 3.0); - const scalar_type f_v1 = xi3 / (xi3 + _cv1 * _cv1 * _cv1); - _nu_t(cell, point) = sa_var * f_v1; - } - else - { - _nu_t(cell, point) = 0.0; - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.cpp deleted file mode 100644 index 6276d59..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp" -#include "VertexCFD_Closure_IncompressibleSpalartAllmarasSource_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleSpalartAllmarasSource) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp deleted file mode 100644 index 1d476aa..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_HPP - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Source term for Spalart-Allmaras turbulence model (SA-neg) -//---------------------------------------------------------------------------// -template -class IncompressibleSpalartAllmarasSource - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleSpalartAllmarasSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField _sa_var; - PHX::MDField _distance; - - PHX::MDField - _grad_sa_var; - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - const double _nu; - const double _sigma; - const double _kappa; - const double _c_b1; - const double _c_b2; - const double _c_t3; - const double _c_t4; - const double _c_v1; - const double _c_v2; - const double _c_v3; - const double _c_w1; - const double _c_w2; - const double _c_w3; - const scalar_type _rlim; - - public: - PHX::MDField _sa_source; - PHX::MDField _sa_prod; - PHX::MDField _sa_dest; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource_impl.hpp deleted file mode 100644 index 5f519d9..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource_impl.hpp +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleSpalartAllmarasSource:: - IncompressibleSpalartAllmarasSource( - const panzer::IntegrationRule& ir, - const FluidProperties::ConstantFluidProperties& fluid_prop) - : _sa_var("spalart_allmaras_variable", ir.dl_scalar) - , _distance("distance", ir.dl_scalar) - , _grad_sa_var("GRAD_spalart_allmaras_variable", ir.dl_vector) - , _nu(fluid_prop.constantKinematicViscosity()) - , _sigma(2.0 / 3.0) - , _kappa(0.41) - , _c_b1(0.1355) - , _c_b2(0.622) - , _c_t3(1.2) - , _c_t4(0.5) - , _c_v1(7.1) - , _c_v2(0.7) - , _c_v3(0.9) - , _c_w1(_c_b1 / (_kappa * _kappa) + (1.0 + _c_b2) / _sigma) - , _c_w2(0.3) - , _c_w3(2.0) - , _rlim(10.0) - , _sa_source("SOURCE_spalart_allmaras_equation", ir.dl_scalar) - , _sa_prod("PRODUCTION_spalart_allmaras_equation", ir.dl_scalar) - , _sa_dest("DESTRUCTION_spalart_allmaras_equation", ir.dl_scalar) -{ - // Add dependent fields - this->addDependentField(_sa_var); - this->addDependentField(_distance); - this->addDependentField(_grad_sa_var); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_sa_source); - this->addEvaluatedField(_sa_prod); - this->addEvaluatedField(_sa_dest); - - // Closure model name - this->setName("Spalart-Allmaras Incompressible Source " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasSource:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleSpalartAllmarasSource::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _sa_var.extent(1); - const double smooth_tol = 1.0e-8; - using std::exp; - using std::max; - using std::pow; - using std::sqrt; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Cache SA variable and wall distance variable values - const auto sa_var = _sa_var(cell, point); - const auto d = _distance(cell, point); - const scalar_type sa_ramp - = SmoothMath::ramp(sa_var, -smooth_tol, smooth_tol); - - // Vorticity calculation - scalar_type S2 = pow(_grad_velocity[0](cell, point, 1) - - _grad_velocity[1](cell, point, 0), - 2.0); - - if (num_space_dim == 3) - { - S2 += pow(_grad_velocity[1](cell, point, 2) - - _grad_velocity[2](cell, point, 1), - 2.0) - + pow(_grad_velocity[2](cell, point, 0) - - _grad_velocity[0](cell, point, 2), - 2.0); - } - - const scalar_type S = SmoothMath::max(sqrt(S2), smooth_tol, 0.0); - - // Functions for production term - const scalar_type chi = sa_var / _nu; - const scalar_type f_v1 = pow(chi, 3.0) - / (pow(chi, 3.0) + pow(_c_v1, 3.0)); - const scalar_type f_v2 = 1.0 - chi / (1.0 + chi * f_v1); - const scalar_type f_t2 = _c_t3 * exp(-_c_t4 * chi * chi); - - // Sbar - const scalar_type Sbar = sa_var * f_v2 / (_kappa * _kappa * d * d); - - // Stilda - scalar_type Stilda = S; - - if (Sbar > -_c_v2 * S) - { - Stilda += Sbar; - } - else - { - Stilda += S * (_c_v2 * _c_v2 * S + _c_v3 * Sbar) - / ((_c_v3 - 2 * _c_v2) * S - Sbar); - } - - // Ensure Stilda is finite - if (sqrt(pow(Stilda, 2.0)) < smooth_tol) - { - if (Stilda < 0) - { - Stilda = -smooth_tol; - } - else - { - Stilda = smooth_tol; - } - } - - // Production term calculation - const scalar_type sa_prod_pos = _c_b1 * (1.0 - f_t2) * Stilda - * sa_var; - const scalar_type sa_prod_neg = _c_b1 * (1.0 - _c_t3) * S * sa_var; - - _sa_prod(cell, point) = sa_ramp * sa_prod_pos - + (1.0 - sa_ramp) * sa_prod_neg; - - // Functions for destruction term - const scalar_type r_sa = sa_var - / (Stilda * _kappa * _kappa * d * d); - const scalar_type r = SmoothMath::min(r_sa, _rlim, smooth_tol); - const scalar_type g = r + _c_w2 * (pow(r, 6.0) - r); - const scalar_type f_w = g - * pow((1.0 + pow(_c_w3, 6.0)) - / (pow(g, 6.0) + pow(_c_w3, 6.0)), - 1.0 / 6.0); - - // Calculation of destruction term - const scalar_type sa_dest_pos - = -(_c_w1 * f_w - _c_b1 * f_t2 / (_kappa * _kappa)) - * pow(sa_var / d, 2.0); - const scalar_type sa_dest_neg = _c_w1 * pow(sa_var / d, 2.0); - - _sa_dest(cell, point) = sa_ramp * sa_dest_pos - + (1.0 - sa_ramp) * sa_dest_neg; - - // Set SA source equal to sum of production and destruction - _sa_source(cell, point) = _sa_prod(cell, point) - + _sa_dest(cell, point); - - // Add SA gradient contribution - for (int i = 0; i < num_space_dim; ++i) - { - _sa_source(cell, point) - += _c_b2 / _sigma * pow(_grad_sa_var(cell, point, i), 2.0); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLESPALARTALLMARASSOURCE_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.cpp deleted file mode 100644 index af0b9bf..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp" -#include "VertexCFD_Closure_IncompressibleVariableConvectiveFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleVariableConvectiveFlux) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp deleted file mode 100644 index f02f741..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Conservative term for incompressible turbulence variable equation -//---------------------------------------------------------------------------// -template -class IncompressibleVariableConvectiveFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleVariableConvectiveFlux( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params, - const std::string& flux_prefix = "", - const std::string& field_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - std::string _variable_name; - std::string _equation_name; - - public: - PHX::MDField _var_flux; - - private: - PHX::MDField _var; - Kokkos::Array, - num_space_dim> - _velocity; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux_impl.hpp deleted file mode 100644 index 1030e72..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux_impl.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_IMPL_HPP - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleVariableConvectiveFlux:: - IncompressibleVariableConvectiveFlux( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params, - const std::string& flux_prefix, - const std::string& field_prefix) - : _variable_name(closure_params.get("Field Name")) - , _equation_name(closure_params.get("Equation Name")) - , _var_flux(flux_prefix + "CONVECTIVE_FLUX_" + _equation_name, ir.dl_vector) - , _var(field_prefix + _variable_name, ir.dl_scalar) -{ - // Evaluated fields - this->addEvaluatedField(_var_flux); - - // Dependent fields - this->addDependentField(_var); - - Utils::addDependentVectorField( - *this, ir.dl_scalar, _velocity, field_prefix + "velocity_"); - - this->setName(_equation_name + " Incompressible Convective Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableConvectiveFlux:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableConvectiveFlux:: -operator()(const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _var_flux.extent(1); - const int num_grad_dim = _var_flux.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - for (int dim = 0; dim < num_grad_dim; ++dim) - { - _var_flux(cell, point, dim) = _var(cell, point) - * _velocity[dim](cell, point); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLECONVECTIVEFLUX_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.cpp deleted file mode 100644 index 6f122ed..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp" -#include "VertexCFD_Closure_IncompressibleVariableDiffusionFlux_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleVariableDiffusionFlux) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp deleted file mode 100644 index d1338d6..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Diffusion term for incompressible turbulence variable equation -//---------------------------------------------------------------------------// -template -class IncompressibleVariableDiffusionFlux - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleVariableDiffusionFlux( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params, - const std::string& flux_prefix = "", - const std::string& gradient_prefix = ""); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - std::string _variable_name; - std::string _equation_name; - - public: - PHX::MDField - _var_diff_flux; - - private: - PHX::MDField _diffusivity_var; - PHX::MDField - _grad_var; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux_impl.hpp deleted file mode 100644 index 02e76b1..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux_impl.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_IMPL_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleVariableDiffusionFlux:: - IncompressibleVariableDiffusionFlux( - const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& closure_params, - const std::string& flux_prefix, - const std::string& gradient_prefix) - : _variable_name(closure_params.get("Field Name")) - , _equation_name(closure_params.get("Equation Name")) - , _var_diff_flux(flux_prefix + "DIFFUSION_FLUX_" + _equation_name, - ir.dl_vector) - , _diffusivity_var("diffusivity_" + _variable_name, ir.dl_scalar) - , _grad_var(gradient_prefix + "GRAD_" + _variable_name, ir.dl_vector) -{ - // Add evaludated fields - this->addEvaluatedField(_var_diff_flux); - - // Add dependent fields - this->addDependentField(_diffusivity_var); - this->addDependentField(_grad_var); - - // Closure model name - this->setName(_equation_name + " Incompressible Diffusion Flux " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableDiffusionFlux:: - evaluateFields(typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleVariableDiffusionFlux::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - const int cell = team.league_rank(); - const int num_point = _var_diff_flux.extent(1); - const int num_grad_dim = _var_diff_flux.extent(2); - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Loop over spatial dimension - for (int i = 0; i < num_grad_dim; ++i) - { - _var_diff_flux(cell, point, i) = _diffusivity_var(cell, point) - * _grad_var(cell, point, i); - } - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_CLOSURE_INCOMPRESSIBLEVARIABLEDIFFUSIONFLUX_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.cpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.cpp deleted file mode 100644 index c421eca..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp" - -#include "VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp" -#include "VertexCFD_Closure_IncompressibleWALEEddyViscosity_impl.hpp" - -VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM( - VertexCFD::ClosureModel::IncompressibleWALEEddyViscosity) diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp deleted file mode 100644 index e6dbd41..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_HPP - -#include -#include - -#include -#include -#include -#include - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -// Turbulent eddy viscosity for WALE LES turbulence model -//---------------------------------------------------------------------------// -template -class IncompressibleWALEEddyViscosity - : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - - IncompressibleWALEEddyViscosity(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params); - - void evaluateFields(typename Traits::EvalData workset) override; - - KOKKOS_INLINE_FUNCTION - void operator()( - const Kokkos::TeamPolicy::member_type& team) const; - - private: - PHX::MDField - _element_length; - - Kokkos::Array< - PHX::MDField, - num_space_dim> - _grad_velocity; - - double _C_k; - double _C_w; - - public: - PHX::MDField _k_sgs; - PHX::MDField _nu_t; -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity_impl.hpp deleted file mode 100644 index 7ced17a..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity_impl.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_IMPL_HPP -#define VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_IMPL_HPP - -#include "utils/VertexCFD_Utils_SmoothMath.hpp" -#include "utils/VertexCFD_Utils_VectorField.hpp" - -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -IncompressibleWALEEddyViscosity:: - IncompressibleWALEEddyViscosity(const panzer::IntegrationRule& ir, - const Teuchos::ParameterList& user_params) - : _element_length("les_element_length", ir.dl_vector) - , _C_k(0.094) - , _C_w(0.275) - , _k_sgs("sgs_kinetic_energy", ir.dl_scalar) - , _nu_t("turbulent_eddy_viscosity", ir.dl_scalar) -{ - // Check for user-defined coefficients - if (user_params.isSublist("Turbulence Parameters")) - { - Teuchos::ParameterList turb_list - = user_params.sublist("Turbulence Parameters"); - - if (turb_list.isType("C_w")) - { - _C_w = turb_list.get("C_w"); - } - - if (turb_list.isType("C_k")) - { - _C_k = turb_list.get("C_k"); - } - } - - // Add dependent fields - this->addDependentField(_element_length); - - Utils::addDependentVectorField( - *this, ir.dl_vector, _grad_velocity, "GRAD_velocity_"); - - // Add evaluated fields - this->addEvaluatedField(_k_sgs); - this->addEvaluatedField(_nu_t); - - // Closure model name - this->setName("Incompressible WALE Turbulent Eddy Viscosity " - + std::to_string(num_space_dim) + "D"); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleWALEEddyViscosity::evaluateFields( - typename Traits::EvalData workset) -{ - auto policy = panzer::HP::inst().teamPolicy( - workset.num_cells); - Kokkos::parallel_for(this->getName(), policy, *this); -} - -//---------------------------------------------------------------------------// -template -void IncompressibleWALEEddyViscosity::operator()( - const Kokkos::TeamPolicy::member_type& team) const -{ - using std::pow; - using std::sqrt; - - const int cell = team.league_rank(); - const int num_point = _nu_t.extent(1); - const auto tol = 1.0e-10; - const double one_third = 1.0 / 3.0; - - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, 0, num_point), [&](const int point) { - // Calculate mag square of symmetric and skew symmetric - // velocity gradient tensors, and mesh scale delta - scalar_type mag_sqr_s = 0.0; - scalar_type mag_sqr_w = 0.0; - double delta = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - mag_sqr_s - += pow(0.5 - * (_grad_velocity[i](cell, point, j) - + _grad_velocity[j](cell, point, i)), - 2.0); - - mag_sqr_w - += pow(0.5 - * (_grad_velocity[i](cell, point, j) - - _grad_velocity[j](cell, point, i)), - 2.0); - } - - delta += pow(_element_length(cell, point, i), 2.0); - } - - mag_sqr_s = SmoothMath::max(mag_sqr_s, tol, 0.0); - mag_sqr_w = SmoothMath::max(mag_sqr_w, tol, 0.0); - delta = sqrt(SmoothMath::max(delta, tol, 0.0)); - - // Calculate traceless symmetric part of square of - // velocity gradient tensor - scalar_type mag_sqr_sd = 0.0; - - for (int i = 0; i < num_space_dim; ++i) - { - for (int j = 0; j < num_space_dim; ++j) - { - scalar_type Sd_ij = 0.0; - - for (int k = 0; k < num_space_dim; ++k) - { - // Symmetric terms - Sd_ij += 0.25 - * (_grad_velocity[i](cell, point, k) - + _grad_velocity[k](cell, point, i)) - * (_grad_velocity[k](cell, point, j) - + _grad_velocity[j](cell, point, k)); - - // Skew symmetric terms - Sd_ij += 0.25 - * (_grad_velocity[i](cell, point, k) - - _grad_velocity[k](cell, point, i)) - * (_grad_velocity[k](cell, point, j) - - _grad_velocity[j](cell, point, k)); - } - - // Subtract trace - if (i == j) - { - Sd_ij -= one_third * (mag_sqr_s - mag_sqr_w); - } - - mag_sqr_sd += pow(Sd_ij, 2.0); - } - } - - mag_sqr_sd = SmoothMath::max(mag_sqr_sd, tol, 0.0); - - // Sub-grid eddy viscosity - _nu_t(cell, point) - = pow(_C_w * delta, 2.0) * pow(mag_sqr_sd, 3.0 / 2.0) - / (pow(mag_sqr_s, 5.0 / 2.0) + pow(mag_sqr_sd, 5.0 / 4.0)); - - // Sub-grid kinetic energy - _k_sgs(cell, point) = pow(_nu_t(cell, point) / (_C_k * delta), 2.0); - }); -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end - // VERTEXCFD_CLOSURE_INCOMPRESSIBLEWALEEDDYVISCOSITY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp deleted file mode 100644 index 931f12c..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_HPP -#define VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_HPP - -#include - -#include - -#include -#include - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -class TurbulenceFactory -{ - public: - static constexpr int num_space_dim = NumSpaceDim; - - void buildClosureModel( - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const std::string& turbulence_model_name, - Teuchos::RCP>>> - evaluators); -}; - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_HPP diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian2d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian2d.cpp deleted file mode 100644 index 95987f9..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian3d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian3d.cpp deleted file mode 100644 index 6ebb8e7..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Hessian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian2d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian2d.cpp deleted file mode 100644 index 36a17ea..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian3d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian3d.cpp deleted file mode 100644 index 005b3ed..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Jacobian3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual2d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual2d.cpp deleted file mode 100644 index f49442f..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual3d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual3d.cpp deleted file mode 100644 index 9537454..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Residual3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent2d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent2d.cpp deleted file mode 100644 index 5da7546..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent2d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent3d.cpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent3d.cpp deleted file mode 100644 index be2f2ce..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_Tangent3d.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "VertexCFD_TurbulenceClosureModelFactory.hpp" -#include "VertexCFD_TurbulenceClosureModelFactory_impl.hpp" - -template class VertexCFD::ClosureModel::TurbulenceFactory; diff --git a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_impl.hpp b/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_impl.hpp deleted file mode 100644 index 968efcb..0000000 --- a/src/turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory_impl.hpp +++ /dev/null @@ -1,255 +0,0 @@ -#ifndef VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_IMPL_HPP -#define VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_IMPL_HPP - -#include "closure_models/VertexCFD_Closure_ElementLength.hpp" -#include "closure_models/VertexCFD_Closure_MeasureElementLength.hpp" -#include "closure_models/VertexCFD_Closure_MetricTensorElementLength.hpp" -#include "closure_models/VertexCFD_Closure_SingularValueElementLength.hpp" - -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonDiffusivityCoefficient.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonSource.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasDiffusivityCoefficient.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasSource.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableConvectiveFlux.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleVariableDiffusionFlux.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleWALEEddyViscosity.hpp" -#include "turbulence_models/closure_models/VertexCFD_TurbulenceClosureModelFactory.hpp" - -#include "incompressible_solver/closure_models/VertexCFD_Closure_IncompressibleVariableTimeDerivative.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" - -namespace VertexCFD -{ -namespace ClosureModel -{ -//---------------------------------------------------------------------------// -template -void TurbulenceFactory::buildClosureModel( - const Teuchos::RCP& ir, - const Teuchos::ParameterList& user_params, - const std::string& turbulence_model_name, - Teuchos::RCP>>> - evaluators) -{ - // Fluid properties - Teuchos::ParameterList fluid_prop_list - = user_params.sublist("Fluid Properties"); - const bool build_temp_equ - = user_params.isType("Build Temperature Equation") - ? user_params.get("Build Temperature Equation") - : false; - const bool build_buoyancy_source - = user_params.isType("Build Buoyancy Source") - ? user_params.get("Build Buoyancy Source") - : false; - const bool build_ind_less_equ - = user_params.isType("Build Inductionless MHD Equation") - ? user_params.get("Build Inductionless MHD Equation") - : false; - fluid_prop_list.set("Build Temperature Equation", build_temp_equ); - fluid_prop_list.set("Build Buoyancy Source", build_buoyancy_source); - fluid_prop_list.set("Build Inductionless MHD Equation", - build_ind_less_equ); - FluidProperties::ConstantFluidProperties incompressible_fluidprop_params - = FluidProperties::ConstantFluidProperties(fluid_prop_list); - - // Field name and variable name for each turbulence model - std::vector turb_names_list_vct; - - if (std::string::npos != turbulence_model_name.find("Spalart-Allmaras")) - { - Teuchos::ParameterList sa_names_list; - sa_names_list.set("Field Name", "spalart_allmaras_variable"); - sa_names_list.set("Equation Name", "spalart_allmaras_equation"); - turb_names_list_vct.push_back(sa_names_list); - } - else if (std::string::npos != turbulence_model_name.find("K-Epsilon")) - { - Teuchos::ParameterList k_names_list; - k_names_list.set("Field Name", "turb_kinetic_energy"); - k_names_list.set("Equation Name", "turb_kinetic_energy_equation"); - turb_names_list_vct.push_back(k_names_list); - - Teuchos::ParameterList epsilon_names_list; - epsilon_names_list.set("Field Name", "turb_dissipation_rate"); - epsilon_names_list.set("Equation Name", - "turb_dissipation_rate_equation"); - turb_names_list_vct.push_back(epsilon_names_list); - } - - // Add generic closure models for each variable in turbulence model - for (auto& turb_names_list : turb_names_list_vct) - { - auto eval_time = Teuchos::rcp( - new IncompressibleVariableTimeDerivative( - *ir, turb_names_list)); - evaluators->push_back(eval_time); - - auto eval_conv = Teuchos::rcp( - new IncompressibleVariableConvectiveFlux( - *ir, turb_names_list)); - evaluators->push_back(eval_conv); - - auto eval_diff = Teuchos::rcp( - new IncompressibleVariableDiffusionFlux( - *ir, turb_names_list)); - evaluators->push_back(eval_diff); - } - - // Spalart-Allmaras closure models - if (std::string::npos != turbulence_model_name.find("Spalart-Allmaras")) - { - auto eval_coeff = Teuchos::rcp( - new IncompressibleSpalartAllmarasDiffusivityCoefficient( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval_coeff); - - auto eval_source = Teuchos::rcp( - new IncompressibleSpalartAllmarasSource( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval_source); - - auto eval_eddy = Teuchos::rcp( - new IncompressibleSpalartAllmarasEddyViscosity( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval_eddy); - } - // K-Epsilon model family closure models - else if (std::string::npos != turbulence_model_name.find("K-Epsilon")) - { - // Realizable K-Epsilon closure models - if (std::string::npos - != turbulence_model_name.find("Realizable K-Epsilon")) - { - auto eval_coeff = Teuchos::rcp( - new IncompressibleKEpsilonDiffusivityCoefficient( - *ir, incompressible_fluidprop_params, 1.0, 1.2)); - evaluators->push_back(eval_coeff); - - auto eval_eddy = Teuchos::rcp( - new IncompressibleRealizableKEpsilonEddyViscosity( - *ir)); - evaluators->push_back(eval_eddy); - - auto eval_source = Teuchos::rcp( - new IncompressibleRealizableKEpsilonSource( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval_source); - } - // Standard K-Epsilon closure models - else - { - auto eval_coeff = Teuchos::rcp( - new IncompressibleKEpsilonDiffusivityCoefficient( - *ir, incompressible_fluidprop_params)); - evaluators->push_back(eval_coeff); - - auto eval_eddy = Teuchos::rcp( - new IncompressibleKEpsilonEddyViscosity( - *ir)); - evaluators->push_back(eval_eddy); - - auto eval_source = Teuchos::rcp( - new IncompressibleKEpsilonSource(*ir)); - evaluators->push_back(eval_source); - } - } - // WALE algebraic LES model - else if (std::string::npos != turbulence_model_name.find("WALE")) - { - // WALE eddy viscosity - auto eval_eddy - = Teuchos::rcp(new IncompressibleWALEEddyViscosity( - *ir, user_params)); - evaluators->push_back(eval_eddy); - - // Delta (mesh length scale) evaluator - const std::string delta_prefix = "les_"; - const auto turb_params - = user_params.isSublist("Turbulence Parameters") - ? user_params.sublist("Turbulence Parameters") - : Teuchos::ParameterList(); - const std::string delta_type - = turb_params.isType("LES Element Length") - ? turb_params.get("LES Element Length") - : "ElementLength"; - - if (delta_type == "ElementLength") - { - auto eval_delta = Teuchos::rcp( - new ElementLength(*ir, delta_prefix)); - - evaluators->push_back(eval_delta); - } - else if (delta_type == "MeasureElementLength") - { - auto eval_delta = Teuchos::rcp( - new MeasureElementLength( - *ir, delta_prefix)); - - evaluators->push_back(eval_delta); - } - else if (delta_type == "MetricTensorElementLength") - { - auto eval_delta = Teuchos::rcp( - new MetricTensorElementLength( - *ir, delta_prefix)); - - evaluators->push_back(eval_delta); - } - else if (delta_type == "SingularValueElementLength") - { - const auto method - = turb_params.get("Element Length Method"); - - auto eval_delta = Teuchos::rcp( - new SingularValueElementLength( - *ir, method, delta_prefix)); - - evaluators->push_back(eval_delta); - } - else - { - std::string msg = "Unknown Delta Closure Model:\n"; - - msg += delta_type; - msg += "\n"; - msg += "Please choose from:\n"; - msg += "ElementLength\n"; - msg += "MeasureElementLength\n"; - msg += "MetricTensorElementLength\n"; - msg += "SingularValueElementLength\n"; - - throw std::runtime_error(msg); - } - } -} - -//---------------------------------------------------------------------------// - -} // end namespace ClosureModel -} // end namespace VertexCFD - -#endif // end VERTEXCFD_TURBULENCECLOSUREMODELFACTORY_IMPL_HPP diff --git a/src/turbulence_models/closure_models/unit_test/CMakeLists.txt b/src/turbulence_models/closure_models/unit_test/CMakeLists.txt deleted file mode 100644 index b9d8fa7..0000000 --- a/src/turbulence_models/closure_models/unit_test/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -VertexCFD_add_tests( - LIBS VertexCFD - NAMES - IncompressibleVariableConvectiveFlux - IncompressibleVariableDiffusionFlux - IncompressibleSpalartAllmarasDiffusivityCoefficient - IncompressibleSpalartAllmarasEddyViscosity - IncompressibleSpalartAllmarasSource - IncompressibleKEpsilonDiffusivityCoefficient - IncompressibleKEpsilonEddyViscosity - IncompressibleKEpsilonSource - IncompressibleKOmegaDiffusivityCoefficient - IncompressibleKOmegaEddyViscosity - IncompressibleKOmegaSource - IncompressibleRealizableKEpsilonEddyViscosity - IncompressibleRealizableKEpsilonSource - IncompressibleWALEEddyViscosity - ) diff --git a/src/turbulence_models/closure_models/unit_test/doc/KEpsilonSource_reference.py b/src/turbulence_models/closure_models/unit_test/doc/KEpsilonSource_reference.py deleted file mode 100644 index 6509da7..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/KEpsilonSource_reference.py +++ /dev/null @@ -1,50 +0,0 @@ -import numpy as np -import math - -# Turbulent quantities -nu_t = 3.0 -k = 4.0 -e = 5.0 - -# KEpsilon model constants -C_1 = 1.44 -C_2 = 1.92 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dims = [2, 3] - -for dim in dims: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - Sij_Sij = 0.0 - - for i in range(0, dim): - for j in range(0, dim): - Sij_Sij += pow(0.5 * (grad_vel[i, j] + grad_vel[j, i]), 2.0) - - print(" Sij_Sij = ", Sij_Sij, "\n") - - k_prod = 2.0 * nu_t * Sij_Sij - k_dest = -e - k_source = k_prod + k_dest - - print(" k prod: ", k_prod) - print(" k dest: ", k_dest) - print(" k source: ", k_source, "\n") - - e_prod = C_1 * e / k * k_prod - e_dest = -C_2 * e * e / k - e_source = e_prod + e_dest - - print(" e prod: ", e_prod) - print(" e dest: ", e_dest) - print(" e source: ", e_source, "\n") diff --git a/src/turbulence_models/closure_models/unit_test/doc/KOmegaEddyViscosity_reference.py b/src/turbulence_models/closure_models/unit_test/doc/KOmegaEddyViscosity_reference.py deleted file mode 100644 index 8195246..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/KOmegaEddyViscosity_reference.py +++ /dev/null @@ -1,48 +0,0 @@ -import numpy as np -import math - -# Turbulent quantities -k = 2.5 -omegas = [1, 10] - -# Wilcox k-w model constants -beta_star = 0.09 -C_lim = 7.0 / 8.0 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - S2 = 0.0 - - # COMMENT: This calculation ignores the divergence of u term - # which will be required for compressible flows - for i in range(0, dim): - for j in range(0, dim): - S2 += pow(0.5 * (grad_vel[i, j] + grad_vel[j, i]), 2.0) - - print(" S2 = ", S2, "\n") - - omega_lim = C_lim * math.sqrt(2.0 * S2 / beta_star) - - print(" Limiting value of omega: ", omega_lim) - - for omega in omegas: - print(" Omega = ", omega) - - omega_tilda = max(omega, omega_lim) - - nu_t = k / omega_tilda - - print(" nu_t = ", nu_t, "\n") diff --git a/src/turbulence_models/closure_models/unit_test/doc/KOmegaSource_reference.py b/src/turbulence_models/closure_models/unit_test/doc/KOmegaSource_reference.py deleted file mode 100644 index 79f759f..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/KOmegaSource_reference.py +++ /dev/null @@ -1,96 +0,0 @@ -import numpy as np -import math - -# Wilcox k-w model constants -beta_star = 0.09 -gamma = 0.52 -beta_0 = 0.0708 -sigma_d = 0.125 - -# Turbulent quantities -nu_t = 1.1 -k = 0.1 -w = 3.3 - -# Turbulent gradients -grad_k_3D = np.array([-0.75, 1.5, -2.25]) -grad_w_3D = np.array([-1.25, 2.5, -3.75]) - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -P_lim = 20 * beta_star * w * k -print("Limiting value of production term: ", P_lim) - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - grad_k = grad_k_3D[:dim] - grad_w = grad_w_3D[:dim] - - P = 0.0 - cross = 0.0 - grad_k_grad_w = 0.0 - chi_w = 0.0 - - # COMMENT: This calculation ignores the divergence of u term - # which will be required for compressible flows - for i in range(0, dim): - grad_k_grad_w += grad_k[i] * grad_w[i] - for j in range(0, dim): - S_ij = 0.5 * (grad_vel[i, j] + grad_vel[j, i]) - P += nu_t * pow(S_ij, 2.0) - for l in range(0, dim): - chi_w += ((0.5 * (grad_vel[i, j] - grad_vel[j, i])) * - (0.5 * (grad_vel[j, l] - grad_vel[l, j])) * - (0.5 * (grad_vel[l, i] + grad_vel[i, l]))) - - if (grad_k_grad_w > 0.0): - cross = sigma_d * grad_k_grad_w / w - - chi_w = abs(chi_w / pow(beta_star * w, 3.0)) - - print(" chi_w = ", chi_w, "\n") - - f_b = (1.0 + 85.0 * chi_w) / (1.0 + 100.0 * chi_w) - beta = beta_0 * f_b - - print(" Unlimited k prod values:\n") - - k_prod = P - k_dest = beta_star * w * k - k_source = k_prod - k_dest - - print(" k prod: ", k_prod, "\n") - print(" k dest: ", k_dest, "\n") - print(" k source: ", k_source, "\n") - - print(" Limited k prod values:\n") - - k_prod = min(P, P_lim) - k_source = k_prod - k_dest - - print(" k prod: ", k_prod, "\n") - print(" k dest: ", k_dest, "\n") - print(" k source: ", k_source, "\n") - - w_prod = gamma * w / k * P - w_dest = beta * w * w - w_cross = cross - w_source = w_prod - w_dest + w_cross - - print(" Omega values (same for both cases):\n") - - print(" w prod: ", w_prod, "\n") - print(" w dest: ", w_dest, "\n") - print(" w cross: ", w_cross, "\n") - print(" w source: ", w_source, "\n") diff --git a/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonEddyViscosity_reference.py b/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonEddyViscosity_reference.py deleted file mode 100644 index 07e5fba..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonEddyViscosity_reference.py +++ /dev/null @@ -1,60 +0,0 @@ -import numpy as np -import math - -# Turbulent quantities -k = 4.0 -e = 5.0 - -# Realizable K-Epsilon model constants -A_0 = 4.0 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dims = [2, 3] - -for dim in dims: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - S2 = 0.0 - Omega2 = 0.0 - W = 0.0 - - # COMMENT: The Omega2 calculation must change if the equations - # are to be solved in a rotating reference frame - for i in range(0, dim): - for j in range(0, dim): - S2 += pow(0.5 * (grad_vel[i, j] + grad_vel[j, i]), 2.0) - Omega2 += pow(0.5 * (grad_vel[i, j] - grad_vel[j, i]), 2.0) - for l in range(0, dim): - W += 1.0 / 8.0 * (grad_vel[i, j] + grad_vel[j, i]) * ( - grad_vel[j, l] + grad_vel[l, j]) * (grad_vel[l, i] + - grad_vel[i, l]) - - W = W / pow(S2, 3.0 / 2.0) - - print(" S2 = ", S2, "\n") - print(" Omega2 = ", Omega2, "\n") - print(" W = ", W, "\n") - - Us = math.sqrt(S2 + Omega2) - phi = 1.0 / 3.0 * math.acos(max(min(math.sqrt(6.0) * W, 1.0), -1.0)) - As = math.sqrt(6.0) * math.cos(phi) - - C_nu = 1.0 / (A_0 + (As * Us * k / e)) - - print(" Us = ", Us, "\n") - print(" phi = ", phi, "\n") - print(" As = ", As, "\n") - print(" C_nu = ", C_nu, "\n") - - nu_t = C_nu * k * k / e - - print(" nu_t = ", nu_t, "\n\n") diff --git a/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonSource_reference.py b/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonSource_reference.py deleted file mode 100644 index 4c83936..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/RealizableKEpsilonSource_reference.py +++ /dev/null @@ -1,63 +0,0 @@ -import numpy as np -import math - -# Fluid properties -nu = 0.25 - -# Turbulent quantities -nu_t = 3.0 -k = 4.0 -e = 5.0 - -# Realizable KEpsilon model constants -C_2 = 1.9 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dims = [2, 3] - -for dim in dims: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - S2 = 0.0 - grad_u_sqr = 0.0 - - for i in range(0, dim): - for j in range(0, dim): - grad_u_sqr += pow(grad_vel[i, j], 2.0) - S2 += pow(0.5 * (grad_vel[i, j] + grad_vel[j, i]), 2.0) - - S = math.sqrt(2 * S2) - - print(" S = ", S, "\n") - print(" grad_u_sqr = ", grad_u_sqr, "\n") - - eta = S * k / e - C_1 = max(0.43, eta / (5 + eta)) - - print(" eta = ", eta, "\n") - print(" C_1 = ", C_1, "\n") - - k_prod = nu_t * grad_u_sqr - k_dest = -e - k_source = k_prod + k_dest - - print(" k prod: ", k_prod) - print(" k dest: ", k_dest) - print(" k source: ", k_source, "\n") - - e_prod = C_1 * S * e - e_dest = -C_2 * pow(e, 2.0) / (k + math.sqrt(nu * e)) - e_source = e_prod + e_dest - - print(" e prod: ", e_prod) - print(" e dest: ", e_dest) - print(" e source: ", e_source, "\n") diff --git a/src/turbulence_models/closure_models/unit_test/doc/SADiffusivityCoefficient_reference.py b/src/turbulence_models/closure_models/unit_test/doc/SADiffusivityCoefficient_reference.py deleted file mode 100644 index 25a4502..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/SADiffusivityCoefficient_reference.py +++ /dev/null @@ -1,19 +0,0 @@ -nu = 0.25 -cn1 = 16.0 -sigma = 2.0 / 3.0 - -# Negative SA variable -sa_var = -3.0 -xi = sa_var / nu -xi3 = xi * xi * xi -f_n = (cn1 + xi3) / (cn1 - xi3) - -print("Negative SA variable:", (nu + sa_var * f_n) / sigma) - -# Positive SA variable -sa_var = 3.0 -xi = sa_var / nu -xi3 = xi * xi * xi -f_n = 1.0 - -print("Positive SA variable:", (nu + sa_var * f_n) / sigma) diff --git a/src/turbulence_models/closure_models/unit_test/doc/SAEddyViscosity_reference.py b/src/turbulence_models/closure_models/unit_test/doc/SAEddyViscosity_reference.py deleted file mode 100644 index a9d567f..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/SAEddyViscosity_reference.py +++ /dev/null @@ -1,16 +0,0 @@ -cv1 = 7.1 -nu = 0.25 - -# Negative SA variable with or without temperature equation -sa_var = -3.0 - -print("Negative SA variable:", 0.0) - -# Positive SA variable but larger than max tolerance without temperature equation -sa_var = 3.0 -xi = sa_var / nu -xi3 = xi * xi * xi -f_v1 = xi3 / (xi3 + cv1 * cv1 * cv1) - -print("Positive SA variable (larger then tolerrance). SA Eddy viscosity is:", - sa_var * f_v1) diff --git a/src/turbulence_models/closure_models/unit_test/doc/SASource_reference.py b/src/turbulence_models/closure_models/unit_test/doc/SASource_reference.py deleted file mode 100644 index 7e94b9d..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/SASource_reference.py +++ /dev/null @@ -1,102 +0,0 @@ -import numpy as np -import math - -# Thermophysical constants -nu = 0.25 -wall_dist = 0.1 -sa_vars = [-3.0, 3.0] - -# SA model constants -sigma = 2.0 / 3.0 -kappa = 0.41 -cb1 = 0.1355 -cb2 = 0.622 -ct3 = 1.2 -ct4 = 0.5 -cv1 = 7.1 -cv2 = 0.7 -cv3 = 0.9 -cw1 = cb1 / kappa / kappa + (1.0 + cb2) / sigma -cw2 = 0.3 -cw3 = 2.0 -rlim = 10 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dims = [2, 3] - -for dim in dims: - for sa_var in sa_vars: - print("Computing turbulence quantities in ", dim, "D") - print(" with sa_var = ", sa_var) - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - S2 = pow(grad_vel[1, 0] - grad_vel[0, 1], 2.0) - - if (dim == 3): - S2 += (pow(grad_vel[2, 1] - grad_vel[1, 2], 2.0) + - pow(grad_vel[0, 2] - grad_vel[2, 0], 2.0)) - - S = math.sqrt(S2) - - print(" S = ", S) - - grad_sa = 3 * grad_vel[0] - - sa_source = cb2 / sigma * np.dot(grad_sa, grad_sa) - - print(" grad sa contributuion: ", sa_source) - - chi = sa_var / nu - - fv1 = pow(chi, 3.0) / (pow(chi, 3.0) + pow(cv1, 3.0)) - fv2 = 1.0 - chi / (1 + chi * fv1) - - ft2 = ct3 * math.exp(-ct4 * chi * chi) - - Sbar = sa_var * fv2 / kappa / kappa / wall_dist / wall_dist - - Stilda = 0 - - if (Sbar > -cv2 * S): - Stilda = S + Sbar - else: - Stilda = S + S * (cv2 * cv2 * S + cv3 * Sbar) / ( - (cv3 - 2 * cv2) * S - Sbar) - - # Calculate production term - sa_prod = 0.0 - if (sa_var > 0): - sa_prod = cb1 * (1.0 - ft2) * Stilda * sa_var - else: - sa_prod = cb1 * (1.0 - ct3) * S * sa_var - - print(" sa_prod: ", sa_prod) - - # Calculations for destruction term - r = min(sa_var / Stilda / kappa / kappa / wall_dist / wall_dist, rlim) - g = r + cw2 * (pow(r, 6.0) - r) - fw = g * pow( - (1.0 + pow(cw3, 6.0)) / (pow(g, 6.0) + pow(cw3, 6.0)), 1.0 / 6.0) - - # Calculate destruction term - sa_dest = 0.0 - if (sa_var > 0): - sa_dest = -(cw1 * fw - cb1 * ft2 / kappa / kappa) * pow( - sa_var / wall_dist, 2.0) - else: - sa_dest = cw1 * pow(sa_var / wall_dist, 2.0) - print(" sa_dest: ", sa_dest) - - # Add production and destruction terms - sa_source += (sa_prod + sa_dest) - - print(" total SA source: ", sa_source) - print("") diff --git a/src/turbulence_models/closure_models/unit_test/doc/WALEEddyViscosity_reference.py b/src/turbulence_models/closure_models/unit_test/doc/WALEEddyViscosity_reference.py deleted file mode 100644 index 735ffb2..0000000 --- a/src/turbulence_models/closure_models/unit_test/doc/WALEEddyViscosity_reference.py +++ /dev/null @@ -1,53 +0,0 @@ -import numpy as np -import math - -# Realizable K-Epsilon model constants -C_w = 0.500 -C_k = 0.094 - -# 2D and 3D velocity gradients -grad_vel_2D = np.array([[-0.25, 0.5], [-0.5, 1.0]]) -grad_vel_3D = np.array([[-0.25, 0.5, -0.75], [-0.5, 1.0, -1.5], - [-0.125, 0.25, -0.375]]) - -dim_list = [2, 3] - -for dim in dim_list: - print("Computing turbulence quantities in ", dim, "D\n") - - grad_vel = grad_vel_2D - - if (dim == 3): - grad_vel = grad_vel_3D - - # Element length (=3x du/dx_i) - element_length = 3.0 * grad_vel[0, :] - delta = np.linalg.norm(element_length) - - # Find symmetric and antisymmetric velocity gradient tensors - S = 0.5 * (grad_vel + np.transpose(grad_vel)) - W = 0.5 * (grad_vel - np.transpose(grad_vel)) - - # Find magnitude squared of S and W - magSqrS = pow(np.linalg.norm(S), 2.0) - magSqrW = pow(np.linalg.norm(W), 2.0) - - # Construct S_d tensor - S_d = np.matmul(S, S) + np.matmul( - W, W) - 1.0 / 3.0 * np.identity(dim) * (magSqrS - magSqrW) - magSqrSd = pow(np.linalg.norm(S_d), 2.0) - - # Compute sub-grid eddy viscosity - nu_sgs = pow(C_w * delta, 2.0) * pow(magSqrSd, 3.0 / 2.0) / ( - pow(magSqrS, 5.0 / 2.0) + pow(magSqrSd, 5.0 / 4.0)) - - # Compute sub-grid kinetic energy - k_sgs = pow(nu_sgs / C_k / delta, 2.0) - - # Output information - print(" magSqrS = ", magSqrS, "\n") - print(" magSqrW = ", magSqrW, "\n") - print(" magSqrSd = ", magSqrSd, "\n") - print(" delta = ", delta, "\n") - print(" nu_sgs = ", nu_sgs, "\n") - print(" k_sgs = ", k_sgs, "\n\n") diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonDiffusivityCoefficient.cpp deleted file mode 100644 index 3fc42ca..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonDiffusivityCoefficient.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField nu_t; - - Dependencies(const panzer::IntegrationRule& ir) - : nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - { - this->addEvaluatedField(nu_t); - this->setName( - "K-Epsilon Incompressible Diffusivity Coefficient Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - nu_t.deep_copy(1.5); - } -}; - -template -void testEval() -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Set turbulent quantities - const auto nu_t_value = 1.5; - const auto sigma_k = 1.00; - const auto sigma_e = 1.30; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Fluid properties - const auto nu = 0.25; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", nu); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonDiffusivityCoefficient< - EvalType, - panzer::Traits>(ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_diffusivity_var_k); - test_fixture.registerTestField(eval->_diffusivity_var_e); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var_k - = test_fixture.getTestFieldData(eval->_diffusivity_var_k); - const auto fv_var_e - = test_fixture.getTestFieldData(eval->_diffusivity_var_e); - - // Expected values - const int num_point = ir.num_points; - const double exp_diffusivity_var_k = nu + nu_t_value / sigma_k; - const double exp_diffusivity_var_e = nu + nu_t_value / sigma_e; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_diffusivity_var_k, fieldValue(fv_var_k, 0, qp)); - EXPECT_EQ(exp_diffusivity_var_e, fieldValue(fv_var_e, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonDiffusivityCoefficient, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonDiffusivityCoefficient, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonEddyViscosity.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonEddyViscosity.cpp deleted file mode 100644 index c8d5ce6..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonEddyViscosity.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKEpsilonEddyViscosity.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField turb_kinetic_energy; - PHX::MDField turb_dissipation_rate; - - Dependencies(const panzer::IntegrationRule& ir) - : turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - { - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_dissipation_rate); - this->setName( - "K-Epsilon Incompressible Eddy Viscosity Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - turb_kinetic_energy.deep_copy(2.5); - turb_dissipation_rate.deep_copy(1.25); - } -}; - -template -void testEval() -{ - using std::pow; - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Set turbulent quantities - const double C_nu = 0.09; - const double turb_kinetic_energy_value = 2.5; - const double turb_dissipation_rate_value = 1.25; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonEddyViscosity( - ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_nu_t); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_nu_t = test_fixture.getTestFieldData(eval->_nu_t); - - // Expected values - const int num_point = ir.num_points; - const double exp_diffusivity_var = C_nu - * pow(turb_kinetic_energy_value, 2.0) - / turb_dissipation_rate_value; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_diffusivity_var, fieldValue(fv_nu_t, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonEddyViscosity, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonEddyViscosity, jacobian_test) -{ - testEval(); -} -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonSource.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonSource.cpp deleted file mode 100644 index d3a50f8..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKEpsilonSource.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField nu_t; - PHX::MDField turb_kinetic_energy; - PHX::MDField turb_dissipation_rate; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(nu_t); - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_dissipation_rate); - this->setName( - "K-Epsilon Incompressible Source Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "K-Epsilon source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - } - - nu_t(c, qp) = 3.0; - turb_kinetic_energy(c, qp) = 4.0; - turb_dissipation_rate(c, qp) = 5.0; - } - } -}; - -template -void testEval() -{ - const int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleKEpsilonSource(ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_k_source); - test_fixture.registerTestField(eval->_e_source); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var_k - = test_fixture.getTestFieldData(eval->_k_source); - const auto fv_var_e - = test_fixture.getTestFieldData(eval->_e_source); - - // Expected values - double exp_k_source = 1.375; - double exp_e_source = -0.5250000000000004; - - if (num_space_dim == 3) - { - exp_k_source = 9.203125; - exp_e_source = 13.565624999999997; - } - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_k_source, fieldValue(fv_var_k, 0, qp)); - EXPECT_DOUBLE_EQ(exp_e_source, fieldValue(fv_var_e, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonSource2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonSource2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonSource3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKEpsilonSource3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaDiffusivityCoefficient.cpp deleted file mode 100644 index 58f98fe..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaDiffusivityCoefficient.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaDiffusivityCoefficient.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - double _k; - double _w; - - PHX::MDField _turb_kinetic_energy; - PHX::MDField - _turb_specific_dissipation_rate; - - Dependencies(const panzer::IntegrationRule& ir, - const double k, - const double w) - : _k(k) - , _w(w) - , _turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , _turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - { - this->addEvaluatedField(_turb_kinetic_energy); - this->addEvaluatedField(_turb_specific_dissipation_rate); - this->setName( - "K-Omega Incompressible Diffusivity Coefficient Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - _turb_kinetic_energy.deep_copy(_k); - _turb_specific_dissipation_rate.deep_copy(_w); - } -}; - -template -void testEval() -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Set turbulent quantities - const double k_value = 1.5; - const double w_value = 3.5; - const double sigma_k = 0.7; - const double sigma_w = 0.65; - - // Create parameter list for user-defined constants - Teuchos::ParameterList user_params; - user_params.sublist("Turbulence Parameters") - .sublist("K-Omega Parameters") - .set("sigma_k", sigma_k); - user_params.sublist("Turbulence Parameters") - .sublist("K-Omega Parameters") - .set("sigma_w", sigma_w); - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, k_value, w_value)); - test_fixture.registerEvaluator(deps); - - // Fluid properties - const double nu = 0.25; - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", nu); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleKOmegaDiffusivityCoefficient< - EvalType, - panzer::Traits>(ir, fluid_prop, user_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_diffusivity_var_k); - test_fixture.registerTestField(eval->_diffusivity_var_w); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var_k - = test_fixture.getTestFieldData(eval->_diffusivity_var_k); - const auto fv_var_w - = test_fixture.getTestFieldData(eval->_diffusivity_var_w); - - // Expected values - const int num_point = ir.num_points; - const double exp_diffusivity_var_k = nu + sigma_k * k_value / w_value; - const double exp_diffusivity_var_w = nu + sigma_w * k_value / w_value; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_diffusivity_var_k, fieldValue(fv_var_k, 0, qp)); - EXPECT_DOUBLE_EQ(exp_diffusivity_var_w, fieldValue(fv_var_w, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaDiffusivityCoefficient, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaDiffusivityCoefficient, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaEddyViscosity.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaEddyViscosity.cpp deleted file mode 100644 index 9d229fb..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaEddyViscosity.cpp +++ /dev/null @@ -1,171 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaEddyViscosity.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField turb_kinetic_energy; - PHX::MDField - turb_specific_dissipation_rate; - - const bool _limited; - - Dependencies(const panzer::IntegrationRule& ir, const bool limited) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - , _limited(limited) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_specific_dissipation_rate); - this->setName( - "Incompressible K-Omega Eddy Viscosity Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "K-Omega eddy viscosity test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - } - - turb_kinetic_energy(c, qp) = 2.5; - turb_specific_dissipation_rate(c, qp) = _limited ? 1.0 : 10.0; - } - } -}; - -template -void testEval(const bool limited) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir, limited)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleKOmegaEddyViscosity(ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_nu_t); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_nu_t = test_fixture.getTestFieldData(eval->_nu_t); - - // Expected values - const int num_point = ir.num_points; - double exp_var = 0.25; - if (limited) - { - exp_var = num_space_dim == 3 ? 0.393932564311835 : 0.5879951490600304; - } - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_var, fieldValue(fv_nu_t, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosity2D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosity2D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosity3D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosity3D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosityLimited2D, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosityLimited2D, jacobian_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosityLimited3D, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaEddyViscosityLimited3D, jacobian_test) -{ - testEval(true); -} -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaSource.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaSource.cpp deleted file mode 100644 index 982c9e1..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleKOmegaSource.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "VertexCFD_EvaluatorTestHarness.hpp" -#include "closure_models/unit_test/VertexCFD_ClosureModelFactoryTestHarness.hpp" - -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleKOmegaSource.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - static constexpr int num_space_dim = NumSpaceDim; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField turb_eddy_viscosity; - PHX::MDField turb_kinetic_energy; - PHX::MDField - turb_specific_dissipation_rate; - - PHX::MDField grad_k; - PHX::MDField grad_w; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , turb_eddy_viscosity("turbulent_eddy_viscosity", ir.dl_scalar) - , turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_specific_dissipation_rate("turb_specific_dissipation_rate", - ir.dl_scalar) - , grad_k("GRAD_turb_kinetic_energy", ir.dl_vector) - , grad_w("GRAD_turb_specific_dissipation_rate", ir.dl_vector) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(turb_eddy_viscosity); - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_specific_dissipation_rate); - this->addEvaluatedField(grad_k); - this->addEvaluatedField(grad_w); - this->setName("Incompressible K-Omega Source Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "K-Omega source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - - grad_k(c, qp, dim) = 0.750 * dimqp; - grad_w(c, qp, dim) = 1.250 * dimqp; - } - - turb_eddy_viscosity(c, qp) = 1.1; - turb_kinetic_energy(c, qp) = 0.1; - turb_specific_dissipation_rate(c, qp) = 3.3; - } - } -}; - -template -void testEval(const bool limited) -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Create parameter list for user-defined constants - Teuchos::ParameterList user_params; - user_params.sublist("Turbulence Parameters") - .set("Limit Production Term", limited); - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel:: - IncompressibleKOmegaSource( - ir, user_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_k_source); - test_fixture.registerTestField(eval->_w_source); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_k_source - = test_fixture.getTestFieldData(eval->_k_source); - const auto fv_w_source - = test_fixture.getTestFieldData(eval->_w_source); - - // Expected values - double exp_k_source = num_space_dim == 3 ? 2.57420625 : 1.13905; - const double exp_w_source = num_space_dim == 3 ? 44.524757611667624 - : 19.57778525141911; - if (limited) - { - exp_k_source = num_space_dim == 3 ? 0.5643 : 0.5643; - } - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_k_source, fieldValue(fv_k_source, 0, qp)); - EXPECT_DOUBLE_EQ(exp_w_source, fieldValue(fv_w_source, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSource2D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSource2D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSource3D, residual_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSource3D, jacobian_test) -{ - testEval(false); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSourceLimited2D, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSourceLimited2D, jacobian_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSourceLimited3D, residual_test) -{ - testEval(true); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleKOmegaSourceLimited3D, jacobian_test) -{ - testEval(true); -} -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonEddyViscosity.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonEddyViscosity.cpp deleted file mode 100644 index 52c9ec5..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonEddyViscosity.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField turb_kinetic_energy; - PHX::MDField turb_dissipation_rate; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_dissipation_rate); - this->setName( - "Incompressible Realizable K-Epsilon Eddy Viscosity Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "Realizable K-Epsilon Eddy Viscosity test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - } - - turb_kinetic_energy(c, qp) = 4.0; - turb_dissipation_rate(c, qp) = 5.0; - } - } -}; - -template -void testEval() -{ - const int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleRealizableKEpsilonEddyViscosity< - EvalType, - panzer::Traits, - NumSpaceDim>(ir)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_nu_t); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var = test_fixture.getTestFieldData(eval->_nu_t); - - // Expected values - const int num_point = ir.num_points; - - const double exp_nu_t = num_space_dim == 3 ? 0.3930283177441859 - : 0.496163282309383; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_nu_t, fieldValue(fv_var, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonEddyViscosity2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonEddyViscosity2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonEddyViscosity3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonEddyViscosity3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonSource.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonSource.cpp deleted file mode 100644 index 636a4e5..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleRealizableKEpsilonSource.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleRealizableKEpsilonSource.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - - PHX::MDField nu_t; - PHX::MDField turb_kinetic_energy; - PHX::MDField turb_dissipation_rate; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , nu_t("turbulent_eddy_viscosity", ir.dl_scalar) - , turb_kinetic_energy("turb_kinetic_energy", ir.dl_scalar) - , turb_dissipation_rate("turb_dissipation_rate", ir.dl_scalar) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(nu_t); - this->addEvaluatedField(turb_kinetic_energy); - this->addEvaluatedField(turb_dissipation_rate); - this->setName( - "Realizable K-Epsilon Incompressible Source Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "Realizable K-Epsilon source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - } - - nu_t(c, qp) = 3.0; - turb_kinetic_energy(c, qp) = 4.0; - turb_dissipation_rate(c, qp) = 5.0; - } - } -}; - -template -void testEval() -{ - const int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Fluid properties - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.25); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleRealizableKEpsilonSource( - ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_k_source); - test_fixture.registerTestField(eval->_e_source); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var_k - = test_fixture.getTestFieldData(eval->_k_source); - const auto fv_var_e - = test_fixture.getTestFieldData(eval->_e_source); - - // Expected values - const double exp_k_source = num_space_dim == 2 ? -0.3125 : 8.78125; - const double exp_e_source = num_space_dim == 2 ? -6.146770850376922 - : -4.602804412745295; - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_k_source, fieldValue(fv_var_k, 0, qp)); - EXPECT_DOUBLE_EQ(exp_e_source, fieldValue(fv_var_e, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonSource2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonSource2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonSource3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleRealizableKEpsilonSource3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasDiffusivityCoefficient.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasDiffusivityCoefficient.cpp deleted file mode 100644 index a540d1d..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasDiffusivityCoefficient.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField sa_var; - const double _sa_var_value; - - Dependencies(const panzer::IntegrationRule& ir, const double sa_var_value) - : sa_var("spalart_allmaras_variable", ir.dl_scalar) - , _sa_var_value(sa_var_value) - { - this->addEvaluatedField(sa_var); - this->setName( - "Spalart-Allmaras Incompressible Diffusivity Coefficient Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - sa_var.deep_copy(_sa_var_value); - } -}; - -template -void testEval(const double sa_var_value) -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, sa_var_value)); - test_fixture.registerEvaluator(deps); - - // Closure parameters - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.25); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleSpalartAllmarasDiffusivityCoefficient< - EvalType, - panzer::Traits>(ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_diffusivity_var); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var - = test_fixture.getTestFieldData(eval->_diffusivity_var); - - // Expected values - const int num_point = ir.num_points; - const double exp_diffusivity_var = sa_var_value < 0 ? 4.79243119266055 - : 4.875; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_EQ(exp_diffusivity_var, fieldValue(fv_var, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSADiffusivityCoefficientPositive, residual_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSADiffusivityCoefficientPositive, jacobian_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSADiffusivityCoefficientNegative, residual_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSADiffusivityCoefficientNegative, jacobian_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasEddyViscosity.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasEddyViscosity.cpp deleted file mode 100644 index 0c813e5..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasEddyViscosity.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include "turbulence_models/closure_models/VertexCFD_Closure_IncompressibleSpalartAllmarasEddyViscosity.hpp" - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField sa_var; - const double _sa_var_value; - - Dependencies(const panzer::IntegrationRule& ir, const double sa_var_value) - : sa_var("spalart_allmaras_variable", ir.dl_scalar) - , _sa_var_value(sa_var_value) - { - this->addEvaluatedField(sa_var); - this->setName( - "Spalart-Allmaras Incompressible Eddy Viscosity Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData /**d**/) override - { - sa_var.deep_copy(_sa_var_value); - } -}; - -template -void testEval(const double sa_var_value) -{ - const int num_space_dim = 2; - const int integration_order = 1; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, sa_var_value)); - test_fixture.registerEvaluator(deps); - - // Closure parameters - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.25); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleSpalartAllmarasEddyViscosity< - EvalType, - panzer::Traits>(ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_nu_t); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_nu_t = test_fixture.getTestFieldData(eval->_nu_t); - - // Expected values - const int num_point = ir.num_points; - const double exp_diffusivity_var = sa_var_value < 0 ? 0.0 - : 2.485245055997116; - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_diffusivity_var, fieldValue(fv_nu_t, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSAEddyViscosityPositive, residual_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSAEddyViscosityPositive, jacobian_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSAEddyViscosityNegative, residual_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSAEddyViscosityNegative, jacobian_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasSource.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasSource.cpp deleted file mode 100644 index 568398a..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleSpalartAllmarasSource.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include - -#include "incompressible_solver/fluid_properties/VertexCFD_ConstantFluidProperties.hpp" -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - PHX::MDField grad_sa_var; - - PHX::MDField sa_var; - PHX::MDField wall_dist; - - const double _sa_var_value; - - Dependencies(const panzer::IntegrationRule& ir, const double sa_var_value) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , grad_sa_var("GRAD_spalart_allmaras_variable", ir.dl_vector) - , sa_var("spalart_allmaras_variable", ir.dl_scalar) - , wall_dist("distance", ir.dl_scalar) - , _sa_var_value(sa_var_value) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(grad_sa_var); - this->addEvaluatedField(sa_var); - this->addEvaluatedField(wall_dist); - this->setName( - "Spalart-Allmaras Incompressible Source Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "SA source test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - - grad_sa_var(c, qp, dim) = 0.750 * dimqp; - } - - sa_var(c, qp) = _sa_var_value; - wall_dist(c, qp) = 0.1; - } - } -}; - -template -void testEval(const double sa_var_value) -{ - const int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps - = Teuchos::rcp(new Dependencies(ir, sa_var_value)); - test_fixture.registerEvaluator(deps); - - // Closure parameters - Teuchos::ParameterList fluid_prop_list; - fluid_prop_list.set("Kinematic viscosity", 0.25); - fluid_prop_list.set("Artificial compressibility", 2.0); - fluid_prop_list.set("Build Temperature Equation", false); - const FluidProperties::ConstantFluidProperties fluid_prop(fluid_prop_list); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleSpalartAllmarasSource( - ir, fluid_prop)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_sa_source); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var - = test_fixture.getTestFieldData(eval->_sa_source); - - // Expected values - const int num_point = ir.num_points; - - double exp_sa_source = 0.0; - - if (num_space_dim < 3) - exp_sa_source = sa_var_value < 0 ? 2917.866397598156 - : -5842.742478724074; - else - exp_sa_source = sa_var_value < 0 ? 2922.6799728440574 - : -5837.973707512366; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_sa_source, fieldValue(fv_var, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourcePositive2D, residual_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourcePositive2D, jacobian_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourceNegative2D, residual_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourceNegative2D, jacobian_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourcePositive3D, residual_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourcePositive3D, jacobian_test) -{ - testEval(3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourceNegative3D, residual_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleSASourceNegative3D, jacobian_test) -{ - testEval(-3.0); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableConvectiveFlux.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableConvectiveFlux.cpp deleted file mode 100644 index 8a0222d..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableConvectiveFlux.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField var; - PHX::MDField vel_0; - PHX::MDField vel_1; - PHX::MDField vel_2; - - Dependencies(const panzer::IntegrationRule& ir) - : var("spalart_allmaras_variable", ir.dl_scalar) - , vel_0("velocity_0", ir.dl_scalar) - , vel_1("velocity_1", ir.dl_scalar) - , vel_2("velocity_2", ir.dl_scalar) - - { - this->addEvaluatedField(var); - this->addEvaluatedField(vel_0); - this->addEvaluatedField(vel_1); - this->addEvaluatedField(vel_2); - - this->setName("Variable Convective Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData) override - { - var.deep_copy(2.0); - vel_0.deep_copy(4.0); - vel_1.deep_copy(-5.0); - vel_2.deep_copy(6.0); - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Closure parameters - Teuchos::ParameterList closure_params; - closure_params.set("Field Name", "spalart_allmaras_variable"); - closure_params.set("Equation Name", "spalart_allmaras_equation"); - - // Eval dependencies - auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleVariableConvectiveFlux( - ir, closure_params)); - - // Register and evaluate - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_var_flux); - test_fixture.evaluate(); - const auto fc_var - = test_fixture.getTestFieldData(eval->_var_flux); - - // Expected values - const double exp_var_flux[3] = { - 8.0, - -10.0, - num_space_dim == 3 ? 12.0 : std::numeric_limits::quiet_NaN()}; - - // Assert values - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; dim++) - { - EXPECT_EQ(exp_var_flux[dim], fieldValue(fc_var, 0, qp, dim)); - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableConvectiveFlux2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableConvectiveFlux2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableConvectiveFlux3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableConvectiveFlux3D, jacobian_test) -{ - testEval(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableDiffusionFlux.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableDiffusionFlux.cpp deleted file mode 100644 index f020c5d..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleVariableDiffusionFlux.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - - PHX::MDField grad_var; - PHX::MDField diffusivity_var; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_var("GRAD_spalart_allmaras_variable", ir.dl_vector) - , diffusivity_var("diffusivity_spalart_allmaras_variable", ir.dl_scalar) - { - this->addEvaluatedField(grad_var); - this->addEvaluatedField(diffusivity_var); - this->setName( - "Incompressible Variable Diffusion Flux Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - diffusivity_var.deep_copy(2.0); - - Kokkos::parallel_for( - "incompressible variable diffusion flux test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_var.extent(1); - const int num_space_dim = grad_var.extent(2); - using std::pow; - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - grad_var(c, qp, dim) = 13.0 * (dim + 1); - } - } - } -}; - -template -void testEval() -{ - constexpr int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Closure parameters - Teuchos::ParameterList closure_params; - closure_params.set("Field Name", "spalart_allmaras_variable"); - closure_params.set("Equation Name", "spalart_allmaras_equation"); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleVariableDiffusionFlux( - ir, closure_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_var_diff_flux); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_var - = test_fixture.getTestFieldData(eval->_var_diff_flux); - - // Expected values - const int num_point = ir.num_points; - const double exp_var_diff_flux[3] = { - 26.0, - 52.0, - num_space_dim == 2 ? std::numeric_limits::quiet_NaN() : 78.0}; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - EXPECT_EQ(exp_var_diff_flux[dim], fieldValue(fv_var, 0, qp, dim)); - } - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableDiffusionFlux2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableDiffusionFlux2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableDiffusionFlux3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleVariableDiffusionFlux3D, jacobian_test) -{ - testEval(); -} - -} // namespace Test -} // namespace VertexCFD diff --git a/src/turbulence_models/closure_models/unit_test/tstIncompressibleWALEEddyViscosity.cpp b/src/turbulence_models/closure_models/unit_test/tstIncompressibleWALEEddyViscosity.cpp deleted file mode 100644 index fa086f1..0000000 --- a/src/turbulence_models/closure_models/unit_test/tstIncompressibleWALEEddyViscosity.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Test -{ -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - const double _nanval = std::numeric_limits::quiet_NaN(); - - PHX::MDField grad_vel_0; - PHX::MDField grad_vel_1; - PHX::MDField grad_vel_2; - PHX::MDField element_length; - - Dependencies(const panzer::IntegrationRule& ir) - : grad_vel_0("GRAD_velocity_0", ir.dl_vector) - , grad_vel_1("GRAD_velocity_1", ir.dl_vector) - , grad_vel_2("GRAD_velocity_2", ir.dl_vector) - , element_length("les_element_length", ir.dl_vector) - { - this->addEvaluatedField(grad_vel_0); - this->addEvaluatedField(grad_vel_1); - this->addEvaluatedField(grad_vel_2); - this->addEvaluatedField(element_length); - this->setName( - "Incompressible WALE Eddy Viscosity Unit " - "Test " - "Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData d) override - { - Kokkos::parallel_for( - "Incompressible WALE eddy viscosity test dependencies", - Kokkos::RangePolicy(0, d.num_cells), - *this); - } - - KOKKOS_INLINE_FUNCTION void operator()(const int c) const - { - const int num_point = grad_vel_0.extent(1); - const int num_space_dim = grad_vel_0.extent(2); - using std::pow; - - for (int qp = 0; qp < num_point; ++qp) - { - for (int dim = 0; dim < num_space_dim; ++dim) - { - const int sign = pow(-1, dim + 1); - const int dimqp = (dim + 1) * sign; - grad_vel_0(c, qp, dim) = 0.250 * dimqp; - grad_vel_1(c, qp, dim) = 0.500 * dimqp; - grad_vel_2(c, qp, dim) = num_space_dim == 3 ? 0.125 * dimqp - : _nanval; - element_length(c, qp, dim) = 0.750 * dimqp; - } - } - } -}; - -template -void testEval() -{ - const int num_space_dim = NumSpaceDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_space_dim, integration_order, basis_order); - - const auto& ir = *test_fixture.ir; - - // Create parameter list for user-defined constants - Teuchos::ParameterList user_params; - user_params.sublist("Turbulence Parameters").set("C_w", 0.500); - user_params.sublist("Turbulence Parameters").set("C_k", 0.094); - - // Eval dependencies - const auto deps = Teuchos::rcp(new Dependencies(ir)); - test_fixture.registerEvaluator(deps); - - // Initialize and register - auto eval = Teuchos::rcp( - new ClosureModel::IncompressibleWALEEddyViscosity( - ir, user_params)); - test_fixture.registerEvaluator(eval); - test_fixture.registerTestField(eval->_nu_t); - test_fixture.registerTestField(eval->_k_sgs); - test_fixture.evaluate(); - - // Evaluate test fields - const auto fv_nu_t = test_fixture.getTestFieldData(eval->_nu_t); - const auto fv_k_sgs = test_fixture.getTestFieldData(eval->_k_sgs); - - // Expected values - const int num_point = ir.num_points; - - const double exp_nu_t = num_space_dim == 3 ? 0.04137844429829328 - : 0.14112104197373945; - const double exp_k_sgs = num_space_dim == 3 ? 0.024606058225685103 - : 0.8013741154973666; - - // Assert values - for (int qp = 0; qp < num_point; ++qp) - { - EXPECT_DOUBLE_EQ(exp_nu_t, fieldValue(fv_nu_t, 0, qp)); - EXPECT_DOUBLE_EQ(exp_k_sgs, fieldValue(fv_k_sgs, 0, qp)); - } -} - -//-----------------------------------------------------------------// -TEST(IncompressibleWALEEddyViscosity2D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleWALEEddyViscosity2D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleWALEEddyViscosity3D, residual_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -TEST(IncompressibleWALEEddyViscosity3D, jacobian_test) -{ - testEval(); -} - -//-----------------------------------------------------------------// -} // namespace Test -} // namespace VertexCFD diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt deleted file mode 100644 index 8868550..0000000 --- a/src/utils/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -configure_file(VertexCFD_Utils_config.hpp.cmakein VertexCFD_Utils_config.hpp) - -set(UTILS_HEADERS - VertexCFD_EvaluatorBase.hpp - VertexCFD_Utils_Constants.hpp - VertexCFD_Utils_ExplicitTemplateInstantiation.hpp - VertexCFD_Utils_VectorField.hpp - VertexCFD_Utils_ParameterPack.hpp - VertexCFD_Utils_NonlinearSolver.hpp - VertexCFD_Utils_ScalarToVector.hpp - VertexCFD_Utils_SmoothMath.hpp - VertexCFD_Utils_VelocityDim.hpp - VertexCFD_Utils_VelocityLayout.hpp - VertexCFD_Utils_TypeTraits.hpp - VertexCFD_Utils_Version.hpp - VertexCFD_Utils_MatrixMath.hpp - VertexCFD_Utils_VectorizeOutputFieldNames.hpp - ) - -set(UTILS_SOURCES - VertexCFD_Utils_VelocityDim.cpp - VertexCFD_Utils_VelocityLayout.cpp - VertexCFD_Utils_Version.cpp - ) - -add_library(Utils ${UTILS_SOURCES}) - -target_link_libraries(Utils PUBLIC - MPI::MPI_CXX - Kokkos::kokkos - ) - -target_include_directories(Utils - PUBLIC - $ - $ - $) - -install(TARGETS Utils - EXPORT VertexCFDUtilsTargets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -install(EXPORT VertexCFDUtilsTargets - FILE VertexCFDUtilsTargets.cmake - NAMESPACE VertexCFD:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VertexCFD) - -install(FILES ${UTILS_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/VertexCFD_Utils_config.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -include(CMakePackageConfigHelpers) - -if(VertexCFD_ENABLE_TESTING) - add_subdirectory(unit_test) -endif() diff --git a/src/utils/VertexCFD_EvaluatorBase.hpp b/src/utils/VertexCFD_EvaluatorBase.hpp deleted file mode 100644 index 1df9a21..0000000 --- a/src/utils/VertexCFD_EvaluatorBase.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef VERTEXCFD_EVALUATORBASE_HPP -#define VERTEXCFD_EVALUATORBASE_HPP - -#include - -#include -#include -#include - -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -template -class EvaluatorBase : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - public: - using scalar_type = typename EvalType::ScalarT; - - EvaluatorBase(); - - void postRegistrationSetup(typename Traits::SetupData d, - PHX::FieldManager& vm) override; - void preEvaluate(typename Traits::PreEvalData d) override; - void evaluateFields(typename Traits::EvalData d) override; - void postEvaluate(typename Traits::PostEvalData d) override; - - protected: - virtual void postRegistrationSetupImpl(typename Traits::SetupData d, - PHX::FieldManager& vm); - virtual void preEvaluateImpl(typename Traits::PreEvalData d); - virtual void evaluateFieldsImpl(typename Traits::EvalData d) = 0; - virtual void postEvaluateImpl(typename Traits::PostEvalData d); - - private: - Teuchos::RCP _param_update_trigger; -}; - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#include "VertexCFD_EvaluatorBase_impl.hpp" - -#endif // end VERTEXCFD_EVALUATORBASE_HPP diff --git a/src/utils/VertexCFD_EvaluatorBase_impl.hpp b/src/utils/VertexCFD_EvaluatorBase_impl.hpp deleted file mode 100644 index 7bce233..0000000 --- a/src/utils/VertexCFD_EvaluatorBase_impl.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef VERTEXCFD_EVALUATORBASE_IMPL_HPP -#define VERTEXCFD_EVALUATORBASE_IMPL_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -//---------------------------------------------------------------------------// -template -EvaluatorBase::EvaluatorBase() -{ - // Add scalar parameter trigger - auto dummy_layout = Teuchos::rcp(new PHX::MDALayout(0)); - _param_update_trigger = Teuchos::rcp( - new PHX::Tag("scalar_parameter_eval", dummy_layout)); - this->addDependentField(*_param_update_trigger); -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::postRegistrationSetup( - typename Traits::SetupData d, PHX::FieldManager& vm) -{ - this->postRegistrationSetupImpl(d, vm); -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::preEvaluate(typename Traits::PreEvalData d) -{ - this->preEvaluateImpl(d); -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::evaluateFields(typename Traits::EvalData d) -{ - this->evaluateFieldsImpl(d); -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::postEvaluate( - typename Traits::PostEvalData d) -{ - this->postEvaluateImpl(d); -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::postRegistrationSetupImpl( - typename Traits::SetupData, PHX::FieldManager&) -{ -} - -//---------------------------------------------------------------------------// -template -void - EvaluatorBase::preEvaluateImpl(typename Traits::PreEvalData) -{ -} - -//---------------------------------------------------------------------------// -template -void EvaluatorBase::postEvaluateImpl( - typename Traits::PostEvalData) -{ -} - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_EVALUATORBASE_IMPL_HPP diff --git a/src/utils/VertexCFD_Utils_Constants.hpp b/src/utils/VertexCFD_Utils_Constants.hpp deleted file mode 100644 index 0dbd838..0000000 --- a/src/utils/VertexCFD_Utils_Constants.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef VERTEXCFD_UTILS_CONSTANTS_HPP -#define VERTEXCFD_UTILS_CONSTANTS_HPP - -#include -#include - -namespace VertexCFD -{ -namespace Constants -{ -// Generic pi for any floating point type. -template -constexpr T pi_v = std::enable_if_t::value, T>{ - 3.141592653589793238462643383279502884L}; - -// Most common case. -constexpr double pi = pi_v; - -} // namespace Constants -} // namespace VertexCFD - -#endif // VERTEXCFD_UTILS_CONSTANTS_HPP diff --git a/src/utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp b/src/utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp deleted file mode 100644 index 8253e83..0000000 --- a/src/utils/VertexCFD_Utils_ExplicitTemplateInstantiation.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef VERTEXCFD_UTILS_EXPLICITTEMPLATEINSTANTIATION_HPP -#define VERTEXCFD_UTILS_EXPLICITTEMPLATEINSTANTIATION_HPP - -#include -#include - -// Explicit instantation of standard panzer classes. -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL(name) \ - PANZER_INSTANTIATE_TEMPLATE_CLASS_ONE_T(name) - -// Explicit instantation of standard panzer classes. -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS(name) \ - PANZER_INSTANTIATE_TEMPLATE_CLASS_TWO_T(name) - -// Explicit instantation of classes. -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_RESIDUAL_NUMSPACEDIM(name) \ - template class name; \ - template class name; - -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_TANGENT_NUMSPACEDIM(name) \ - template class name; \ - template class name; - -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_JACOBIAN_NUMSPACEDIM(name) \ - template class name; \ - template class name; - -#ifdef Panzer_BUILD_HESSIAN_SUPPORT -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_HESSIAN_NUMSPACEDIM(name) \ - template class name; \ - template class name; -#else -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_HESSIAN_NUMSPACEDIM(name) -#endif - -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_NUMSPACEDIM(name) \ - VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_RESIDUAL_NUMSPACEDIM(name) \ - VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_TANGENT_NUMSPACEDIM(name) \ - VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_JACOBIAN_NUMSPACEDIM(name) \ - VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_HESSIAN_NUMSPACEDIM(name) - -// Explicit instantation of classes. -#define VERTEXCFD_INSTANTIATE_TEMPLATE_CLASS_EVAL_TRAITS_NUMSPACEDIM(name) \ - PANZER_INSTANTIATE_TEMPLATE_CLASS_THREE_T(name, 2) \ - PANZER_INSTANTIATE_TEMPLATE_CLASS_THREE_T(name, 3) - -#endif // end VERTEXCFD_UTILS_EXPLICITTEMPLATEINSTANTIATION_HPP diff --git a/src/utils/VertexCFD_Utils_KokkosFadFixup.hpp b/src/utils/VertexCFD_Utils_KokkosFadFixup.hpp deleted file mode 100644 index 229ea00..0000000 --- a/src/utils/VertexCFD_Utils_KokkosFadFixup.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef VERTEXCFD_UTILS_KOKKOSFADFIXUP_HPP -#define VERTEXCFD_UTILS_KOKKOSFADFIXUP_HPP - -// This file brings in certain Kokkos instantiations that need to precede any -// VertexCFD includes any time a panzer::FunctionalResponse_Builder is used -#include - -#endif // end VERTEXCFD_UTILS_KOKKOSFADFIXUP_HPP diff --git a/src/utils/VertexCFD_Utils_MatrixMath.hpp b/src/utils/VertexCFD_Utils_MatrixMath.hpp deleted file mode 100644 index 77e92b3..0000000 --- a/src/utils/VertexCFD_Utils_MatrixMath.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VERTEXCFD_UTILS_MATRIXMATH_HPP -#define VERTEXCFD_UTILS_MATRIXMATH_HPP - -#include "VertexCFD_Utils_Constants.hpp" -#include "VertexCFD_Utils_TypeTraits.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace MatrixMath -{ -//---------------------------------------------------------------------------// -// LU decomposition (partial pivoting). -// Finds permutation p and rewrites matrix A so that A[[p]] = LU -// Input matrix A must have full rank or division by zero pivot may occur -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION void LUP(Matrix& A, Permutation& p) -{ - using std::abs; - - const int N = p.extent(0); - - using value_type = typename Matrix::value_type; - - // Initialize permutation vector - for (int i = 0; i < N; ++i) - p(i) = i; - - for (int k = 0; k < N; ++k) - { - int pivot = k; - // max is only used for finding the pivot so doesn't need derivatives. - double max = abs(Sacado::ScalarValue::eval(A(p(pivot), k))); - - for (int i = k + 1; i < N; ++i) - { - const int row = p(i); - // temp is only used for finding the pivot so doesn't need - // derivatives. - const double temp - = abs(Sacado::ScalarValue::eval(A(row, k))); - if (temp > max) - { - max = temp; - pivot = i; - } - } - - if (p(pivot) != p(k)) - { - const int swap = p(pivot); - p(pivot) = p(k); - p(k) = swap; - } - - pivot = p(k); - for (int i = k + 1; i < N; ++i) - { - const int row = p(i); - A(row, k) /= A(pivot, k); - for (int j = k + 1; j < N; ++j) - A(row, j) -= A(row, k) * A(pivot, j); - } - } -} - -//---------------------------------------------------------------------------// -// LU solve (partial pivoting). -// Takes matrix A and permutation p such that A[[p]] = LU and solves Ax = b -// Solution vector is stored in b upon completion. -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION void -LUP_solve(const Matrix& LU, const Permutation& p, Vector& work, Vector& b) -{ - const int N = p.extent(0); - - for (int i = 0; i < N; ++i) - work(i) = b(i); - - for (int i = 1; i < N; ++i) - for (int j = 0; j < i; ++j) - work(p(i)) -= LU(p(i), j) * work(p(j)); - - for (int i = N - 1; i >= 0; --i) - { - for (int j = i + 1; j < N; ++j) - work(p(i)) -= LU(p(i), j) * work(p(j)); - - work(p(i)) /= LU(p(i), i); - } - - for (int i = 0; i < N; ++i) - b(i) = work(p(i)); -} -//---------------------------------------------------------------------------// - -} // end namespace MatrixMath -} // end namespace VertexCFD - -#endif // end VERTEXCFD_UTILS_MATRIXMATH_HPP diff --git a/src/utils/VertexCFD_Utils_NonlinearSolver.hpp b/src/utils/VertexCFD_Utils_NonlinearSolver.hpp deleted file mode 100644 index 64be831..0000000 --- a/src/utils/VertexCFD_Utils_NonlinearSolver.hpp +++ /dev/null @@ -1,292 +0,0 @@ -#ifndef VERTEXCFD_UTILS_NONLINEARSOLVER_HPP -#define VERTEXCFD_UTILS_NONLINEARSOLVER_HPP - -#include - -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace Utils -{ -namespace NonlinearSolver -{ -namespace Impl -{ -//---------------------------------------------------------------------------// -// Access an element of the residual. -template -KOKKOS_INLINE_FUNCTION Scalar -f_i(const Kokkos::Array, N>& f_eval, - const int i) -{ - static_assert(N == NumDeriv, "Jacobian must be square"); - return f_eval[i].val(); -} - -//---------------------------------------------------------------------------// -// Access an element of the Jacobian. -template -KOKKOS_INLINE_FUNCTION Scalar -J_ij(const Kokkos::Array, N>& f_eval, - const int i, - const int j) -{ - static_assert(N == NumDeriv, "Jacobian must be square"); - return f_eval[i].fastAccessDx(j); -} - -//---------------------------------------------------------------------------// -// Jacobian determinant. -template -KOKKOS_INLINE_FUNCTION Scalar -detJ(const Kokkos::Array, 1>& f_eval) -{ - return J_ij(f_eval, 0, 0); -} - -//---------------------------------------------------------------------------// -// Jacobian determinant. -template -KOKKOS_INLINE_FUNCTION Scalar -detJ(const Kokkos::Array, 2>& f_eval) -{ - return J_ij(f_eval, 0, 0) * J_ij(f_eval, 1, 1) - - J_ij(f_eval, 0, 1) * J_ij(f_eval, 1, 0); -} - -//---------------------------------------------------------------------------// -// Jacobian determinant. -template -KOKKOS_INLINE_FUNCTION Scalar -detJ(const Kokkos::Array, 3>& f_eval) -{ - return J_ij(f_eval, 0, 0) * J_ij(f_eval, 1, 1) * J_ij(f_eval, 2, 2) - + J_ij(f_eval, 0, 1) * J_ij(f_eval, 1, 2) * J_ij(f_eval, 2, 0) - + J_ij(f_eval, 0, 2) * J_ij(f_eval, 1, 0) * J_ij(f_eval, 2, 1) - - J_ij(f_eval, 0, 2) * J_ij(f_eval, 1, 1) * J_ij(f_eval, 2, 0) - - J_ij(f_eval, 0, 1) * J_ij(f_eval, 1, 0) * J_ij(f_eval, 2, 2) - - J_ij(f_eval, 0, 0) * J_ij(f_eval, 1, 2) * J_ij(f_eval, 2, 1); -} - -//---------------------------------------------------------------------------// -// Jacobian inverse. -template -KOKKOS_INLINE_FUNCTION bool -invJ(Kokkos::Array, 1>& J_inv, - const Kokkos::Array, 1>& f_eval, - const typename Sacado::ScalarType::type degen_j_tol) -{ - using std::abs; - Scalar det_j = detJ(f_eval); - if (abs(Sacado::ScalarValue::eval(det_j)) < degen_j_tol) - { - return false; - } - J_inv[0][0] = 1.0 / det_j; - return true; -} - -//---------------------------------------------------------------------------// -// Jacobian inverse. -template -KOKKOS_INLINE_FUNCTION bool -invJ(Kokkos::Array, 2>& J_inv, - const Kokkos::Array, 2>& f_eval, - const typename Sacado::ScalarType::type degen_j_tol) -{ - using std::abs; - Scalar det_j = detJ(f_eval); - if (abs(Sacado::ScalarValue::eval(det_j)) < degen_j_tol) - { - return false; - } - Scalar det_j_inv = 1.0 / det_j; - J_inv[0][0] = J_ij(f_eval, 1, 1) * det_j_inv; - J_inv[0][1] = -J_ij(f_eval, 0, 1) * det_j_inv; - J_inv[1][0] = -J_ij(f_eval, 1, 0) * det_j_inv; - J_inv[1][1] = J_ij(f_eval, 0, 0) * det_j_inv; - return true; -} - -//---------------------------------------------------------------------------// -// Jacobian inverse. -template -KOKKOS_INLINE_FUNCTION bool -invJ(Kokkos::Array, 3>& J_inv, - const Kokkos::Array, 3>& f_eval, - const typename Sacado::ScalarType::type degen_j_tol) -{ - using std::abs; - Scalar det_j = detJ(f_eval); - if (abs(Sacado::ScalarValue::eval(det_j)) < degen_j_tol) - { - return false; - } - Scalar det_j_inv = 1.0 / det_j; - - J_inv[0][0] = (J_ij(f_eval, 1, 1) * J_ij(f_eval, 2, 2) - - J_ij(f_eval, 1, 2) * J_ij(f_eval, 2, 1)) - * det_j_inv; - J_inv[0][1] = (J_ij(f_eval, 0, 2) * J_ij(f_eval, 2, 1) - - J_ij(f_eval, 0, 1) * J_ij(f_eval, 2, 2)) - * det_j_inv; - J_inv[0][2] = (J_ij(f_eval, 0, 1) * J_ij(f_eval, 1, 2) - - J_ij(f_eval, 0, 2) * J_ij(f_eval, 1, 1)) - * det_j_inv; - - J_inv[1][0] = (J_ij(f_eval, 1, 2) * J_ij(f_eval, 2, 0) - - J_ij(f_eval, 1, 0) * J_ij(f_eval, 2, 2)) - * det_j_inv; - J_inv[1][1] = (J_ij(f_eval, 0, 0) * J_ij(f_eval, 2, 2) - - J_ij(f_eval, 0, 2) * J_ij(f_eval, 2, 0)) - * det_j_inv; - J_inv[1][2] = (J_ij(f_eval, 0, 2) * J_ij(f_eval, 1, 0) - - J_ij(f_eval, 0, 0) * J_ij(f_eval, 1, 2)) - * det_j_inv; - - J_inv[2][0] = (J_ij(f_eval, 1, 0) * J_ij(f_eval, 2, 1) - - J_ij(f_eval, 1, 1) * J_ij(f_eval, 2, 0)) - * det_j_inv; - J_inv[2][1] = (J_ij(f_eval, 0, 1) * J_ij(f_eval, 2, 0) - - J_ij(f_eval, 0, 0) * J_ij(f_eval, 2, 1)) - * det_j_inv; - J_inv[2][2] = (J_ij(f_eval, 0, 0) * J_ij(f_eval, 1, 1) - - J_ij(f_eval, 0, 1) * J_ij(f_eval, 1, 0)) - * det_j_inv; - - return true; -} - -//---------------------------------------------------------------------------// -// Evaluate and differentiate the residual -template -KOKKOS_INLINE_FUNCTION void -evaluateResidual(Kokkos::Array, N>& f_eval, - Kokkos::Array, N>& u, - const Kokkos::Array& x, - const ResidualFunc& F) -{ - static_assert(N == NumDeriv, "Jacobian must be square"); - - // Setup derivatives. - for (std::size_t i = 0; i < N; ++i) - { - u[i] = x[i]; - u[i].diff(i, N); - } - - // Evaluate and differentiate the residual. - F(u, f_eval); -} - -//---------------------------------------------------------------------------// -// Update the solution using Newton's method. -template -KOKKOS_INLINE_FUNCTION bool updateSolution( - Kokkos::Array& x, - Kokkos::Array, N>& J_inv, - const Kokkos::Array, N>& f_eval, - const typename Sacado::ScalarType::type degen_j_tol) -{ - static_assert(N == NumDeriv, "Jacobian must be square"); - - // Invert the Jacobian and check for degeneracy. - bool success = invJ(J_inv, f_eval, degen_j_tol); - if (!success) - { - return false; - } - - // Solve the linear problem: update = J^-1 * -F(u) and - // apply the update: x += update - for (std::size_t i = 0; i < N; ++i) - { - for (std::size_t j = 0; j < N; ++j) - { - x[i] -= J_inv[i][j] * f_i(f_eval, j); - } - } - return true; -} - -//---------------------------------------------------------------------------// - -} // end namespace Impl - -//---------------------------------------------------------------------------// -// Perform a thread-local nonlinear solve with Newton's method. -// -// An initial guess is given by x and the solution is also output in x. -// -// The Residual Func signature accepts a const Kokkos array for x and a -// non-const Kokkos array for the residual evaluation. -// -// This function returns false if convergence was not achieved. -template -KOKKOS_INLINE_FUNCTION bool -solve(Kokkos::Array& x, - const ResidualFunc& F, - const typename Sacado::ScalarType::type newton_tolerance, - const int max_iters) -{ - using std::abs; - using fad_type = Sacado::Fad::SFad; - using value_type = typename Sacado::ScalarType::type; - - // Evaluate the initial residual and Jacobian. - Kokkos::Array u; - Kokkos::Array f_eval; - Impl::evaluateResidual(f_eval, u, x, F); - - // Iterate until converged or maximum iteration count is reached. - Kokkos::Array, N> J_inv; - value_type j_tol = 10.0 * Kokkos::Experimental::epsilon::value; - for (int k = 0; k < max_iters; ++k) - { - // Update the solution. - bool success = Impl::updateSolution(x, J_inv, f_eval, j_tol); - - // Check for degeneracy of the Jacobian. If it is degenerate then the - // problem is ill-conditioned and we don't expect convergence. - if (!success) - { - return false; - } - - // Check for convergence of the absolute infinity norm of the - // residual. - bool is_converged = true; - for (std::size_t i = 0; i < N; ++i) - { - if (newton_tolerance - < abs(Sacado::ScalarValue::eval(f_eval[i]))) - { - is_converged = false; - break; - } - } - if (is_converged) - { - return true; - } - - // Evaluate the residual and Jacobian. - Impl::evaluateResidual(f_eval, u, x, F); - } - - // Convergence was not achieved within the maxiumum number of iterations. - return false; -} - -//---------------------------------------------------------------------------// - -} // end namespace NonlinearSolver -} // end namespace Utils -} // end namespace VertexCFD - -#endif // end VERTEXCFD_UTILS_NONLINEARSOLVER_HPP diff --git a/src/utils/VertexCFD_Utils_ParameterPack.hpp b/src/utils/VertexCFD_Utils_ParameterPack.hpp deleted file mode 100644 index bbbed4e..0000000 --- a/src/utils/VertexCFD_Utils_ParameterPack.hpp +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef VERTEXCFD_PARAMETERPACK_HPP -#define VERTEXCFD_PARAMETERPACK_HPP - -#include - -#include -#include - -namespace VertexCFD -{ -namespace Utils -{ -//---------------------------------------------------------------------------// -// Parameter pack device capture. -// -// NOTE: In general this would not be needed but NVCC cannot capture parameter -// packs in lambda functions hence we need to wrap them in something that can -// be captured. -//---------------------------------------------------------------------------// -// Get the type at the given index of a paremeter pack. -template -struct PackTypeAtIndexImpl; - -template -struct PackTypeAtIndexImpl<0, T, Types...> -{ - using type = T; -}; - -template -struct PackTypeAtIndexImpl -{ - using type = typename PackTypeAtIndexImpl::type; -}; - -template -struct PackTypeAtIndex -{ - using type = typename PackTypeAtIndexImpl::type; - static_assert(N < sizeof...(Types), "Type index out of bounds"); -}; - -//---------------------------------------------------------------------------// -// Parameter pack element. -template -struct ParameterPackElement -{ - T _m; -}; - -//---------------------------------------------------------------------------// -// Capture a parameter pack. All parameter pack elements must be copyable to -// device. -template -struct ParameterPackImpl; - -template -struct ParameterPackImpl, Types...> - : ParameterPackElement... -{ -}; - -template -struct ParameterPack - : ParameterPackImpl, Types...> -{ - template - using value_type = typename PackTypeAtIndex::type; - - template - using const_value_type = typename std::add_const>::type; - - template - using element_type = ParameterPackElement>; - - static constexpr std::size_t size = sizeof...(Types); -}; - -//---------------------------------------------------------------------------// -// Static type checker. -template -struct is_parameter_pack_impl : public std::false_type -{ -}; - -template -struct is_parameter_pack_impl> : public std::true_type -{ -}; - -template -struct is_parameter_pack - : public is_parameter_pack_impl::type>::type -{ -}; - -//---------------------------------------------------------------------------// - -} // end namespace Utils - -//---------------------------------------------------------------------------// -// Get an element from a parameter pack. Note that this has been inserted in -// the main VertexCFD namespace as it will be used heavily. -template -KOKKOS_FORCEINLINE_FUNCTION - typename std::enable_if::value, - typename ParameterPack_t::template value_type&>::type - get(ParameterPack_t& pp) -{ - return static_cast&>(pp) - ._m; -} - -template -KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< - Utils::is_parameter_pack::value, - typename ParameterPack_t::template const_value_type&>::type -get(const ParameterPack_t& pp) -{ - return static_cast&>( - pp) - ._m; -} - -//---------------------------------------------------------------------------// -namespace Utils -{ -//---------------------------------------------------------------------------// -// Fill a parameter pack. Note the indexing is such that the Nth element of a -// parameter pack is the Nth element of the tuple. -template -void fillParameterPackImpl(ParameterPack_t& pp, - const std::integral_constant, - const T& t, - const Types&...) -{ - get(pp) = t; -} - -template -void fillParameterPackImpl(ParameterPack_t& pp, - const std::integral_constant, - const T& t, - const Types&... ts) -{ - get(pp) = t; - fillParameterPackImpl( - pp, std::integral_constant(), ts...); -} - -template -void fillParameterPack(ParameterPack_t& pp, const Types&... ts) -{ - fillParameterPackImpl( - pp, - std::integral_constant(), - ts...); -} - -// Empty case. -template -void fillParameterPack(ParameterPack_t&) -{ -} - -//---------------------------------------------------------------------------// -// Create a parameter pack. -template -ParameterPack makeParameterPack(const Types&... ts) -{ - ParameterPack pp; - fillParameterPack(pp, ts...); - return pp; -} - -//---------------------------------------------------------------------------// - -} // end namespace Utils - -//---------------------------------------------------------------------------// - -} // end namespace VertexCFD - -#endif // end VERTEXCFD_PARAMETERPACK_HPP diff --git a/src/utils/VertexCFD_Utils_ScalarFieldView.hpp b/src/utils/VertexCFD_Utils_ScalarFieldView.hpp deleted file mode 100644 index 15fe161..0000000 --- a/src/utils/VertexCFD_Utils_ScalarFieldView.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef VERTEXCFD_UTILS_SCALARFIELDVIEW_HPP -#define VERTEXCFD_UTILS_SCALARFIELDVIEW_HPP - -#include -#include -#include - -#include -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Utils -{ -//---------------------------------------------------------------------------// -// This function registers an evaluated Kokkos View of scalar fields indexed by -// the number of equations. -template -void addEvaluatedScalarFieldView( - PHX::EvaluatorWithBaseImpl& f, - const Teuchos::RCP& data_layout, - const int num_entries, - Kokkos::View*>& kokkos_view, - const std::string& base_name) -{ - for (int entry = 0; entry < num_entries; ++entry) - { - const std::string name = base_name + std::to_string(entry); - kokkos_view(entry) - = PHX::MDField(name, data_layout); - f.addEvaluatedField(kokkos_view(entry)); - } -} - -//---------------------------------------------------------------------------// -// This function registers a dependent Kokkos View of fields indexed by -// the number of velocity equations. -template -void addDependentScalarFieldView( - PHX::EvaluatorWithBaseImpl& f, - const Teuchos::RCP& data_layout, - const int num_entries, - Kokkos::View*>& kokkos_view, - const std::string& base_name) -{ - for (int entry = 0; entry < num_entries; ++entry) - { - const std::string name = base_name + std::to_string(entry); - kokkos_view(entry) - = PHX::MDField(name, data_layout); - f.addDependentField(kokkos_view(entry)); - } -} - -} // end namespace Utils -} // end namespace VertexCFD - -#endif // end VERTEXCFD_UTILS_SCALARFIELDVIEW_HPP diff --git a/src/utils/VertexCFD_Utils_ScalarToVector.hpp b/src/utils/VertexCFD_Utils_ScalarToVector.hpp deleted file mode 100644 index 2a18b7b..0000000 --- a/src/utils/VertexCFD_Utils_ScalarToVector.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef VERTEXCFD_UTILS_SCALARTOVECTOR_HPP -#define VERTEXCFD_UTILS_SCALARTOVECTOR_HPP - -#include -#include - -#include -#include - -namespace VertexCFD -{ -namespace Utils -{ -//---------------------------------------------------------------------------// -template -class ScalarToVector : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - private: - using ScalarT = typename EvalType::ScalarT; - - public: - ScalarToVector(const panzer::IntegrationRule& ir, - const std::string& field_name, - const int num_scalars, - const bool time_deriv); - - void evaluateFields(typename panzer::Traits::EvalData); - - private: - // Dependent scalar fields - std::vector> - _scalar_fields; - std::vector> - _scalar_dxdt_fields; - std::vector< - PHX::MDField> - _scalar_grad_fields; - - public: - // Evaluated vector fields - PHX::MDField _vector_fields; - PHX::MDField _vector_dxdt_fields; - PHX::MDField - _vector_grad_fields; -}; - -//---------------------------------------------------------------------------// - -} // namespace Utils -} // namespace VertexCFD - -#include "VertexCFD_Utils_ScalarToVector_impl.hpp" - -#endif // VERTEXCFD_UTILS_SCALARTOVECTOR_HPP diff --git a/src/utils/VertexCFD_Utils_ScalarToVector_impl.hpp b/src/utils/VertexCFD_Utils_ScalarToVector_impl.hpp deleted file mode 100644 index 800ffae..0000000 --- a/src/utils/VertexCFD_Utils_ScalarToVector_impl.hpp +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef VERTEXCFD_UTILS_SCALARTOVECTOR_IMPL_HPP -#define VERTEXCFD_UTILS_SCALARTOVECTOR_IMPL_HPP - -#include "VertexCFD_Utils_ScalarToVector.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace Utils -{ -//---------------------------------------------------------------------------// -template -ScalarToVector::ScalarToVector( - const panzer::IntegrationRule& ir, - const std::string& field_name, - const int num_scalars, - const bool time_deriv) -{ - // Build scalar and gradient fields - _scalar_fields.reserve(num_scalars); - _scalar_grad_fields.reserve(num_scalars); - std::string name; - for (int sc = 0; sc < num_scalars; ++sc) - { - name = field_name + "_" + std::to_string(sc); - _scalar_fields.emplace_back(name, ir.dl_scalar); - - name = "GRAD_" + field_name + "_" + std::to_string(sc); - _scalar_grad_fields.emplace_back(name, ir.dl_vector); - } - - // Create new data layout for vector component - Teuchos::RCP vector_layout; - - // field is Cell, Point - vector_layout - = Teuchos::rcp(new PHX::MDALayout( - ir.dl_scalar->extent(0), ir.dl_scalar->extent(1), num_scalars)); - _vector_fields = PHX::MDField( - field_name, vector_layout); - - // GRAD field is Cell, Point, Dim - vector_layout = Teuchos::rcp( - new PHX::MDALayout( - ir.dl_vector->extent(0), - ir.dl_vector->extent(1), - ir.dl_vector->extent(2), - num_scalars)); - _vector_grad_fields - = PHX::MDField( - "GRAD_" + field_name, vector_layout); - - // Add dependent/evaluated fields - this->addEvaluatedField(_vector_fields); - this->addEvaluatedField(_vector_grad_fields); - - for (int sc = 0; sc < num_scalars; ++sc) - { - this->addDependentField(_scalar_fields[sc]); - this->addDependentField(_scalar_grad_fields[sc]); - } - - // Add time-derivative components if requested - if (time_deriv) - { - // Set up scalar fields - _scalar_dxdt_fields.reserve(num_scalars); - for (int sc = 0; sc < num_scalars; ++sc) - { - name = "DXDT_" + field_name + "_" + std::to_string(sc); - _scalar_dxdt_fields.emplace_back(name, ir.dl_scalar); - } - - // Create vector layout/field - vector_layout = Teuchos::rcp( - new PHX::MDALayout( - ir.dl_scalar->extent(0), ir.dl_scalar->extent(1), num_scalars)); - _vector_dxdt_fields - = PHX::MDField( - "DXDT_" + field_name, vector_layout); - - // Register fields - this->addEvaluatedField(_vector_dxdt_fields); - for (int sc = 0; sc < num_scalars; ++sc) - this->addDependentField(_scalar_dxdt_fields[sc]); - } - - this->setName("ScalarToVector"); -} - -//---------------------------------------------------------------------------// -template -void ScalarToVector::evaluateFields( - typename panzer::Traits::EvalData) -{ - const int num_scalars = _scalar_fields.size(); - - // Process scalars sequentially - for (int sc = 0; sc < num_scalars; ++sc) - { - // Copy field - auto scalar_field_view = _scalar_fields[sc].get_view(); - auto vector_field_view = Kokkos::subview( - _vector_fields.get_view(), Kokkos::ALL(), Kokkos::ALL(), sc); - Kokkos::deep_copy(vector_field_view, scalar_field_view); - - if (_scalar_dxdt_fields.size() > 0) - { - // Copy DXDT field - auto scalar_dxdt_field_view = _scalar_dxdt_fields[sc].get_view(); - auto vector_dxdt_field_view - = Kokkos::subview(_vector_dxdt_fields.get_view(), - Kokkos::ALL(), - Kokkos::ALL(), - sc); - Kokkos::deep_copy(vector_dxdt_field_view, scalar_dxdt_field_view); - } - - // Copy GRAD field - auto scalar_grad_field_view = _scalar_grad_fields[sc].get_view(); - auto vector_grad_field_view - = Kokkos::subview(_vector_grad_fields.get_view(), - Kokkos::ALL(), - Kokkos::ALL(), - Kokkos::ALL(), - sc); - Kokkos::deep_copy(vector_grad_field_view, scalar_grad_field_view); - } -} - -//---------------------------------------------------------------------------// - -} // namespace Utils -} // namespace VertexCFD - -#endif // VERTEXCFD_UTILS_SCALARTOVECTOR_IMPL_HPP diff --git a/src/utils/VertexCFD_Utils_SmoothMath.hpp b/src/utils/VertexCFD_Utils_SmoothMath.hpp deleted file mode 100644 index 8ebd32b..0000000 --- a/src/utils/VertexCFD_Utils_SmoothMath.hpp +++ /dev/null @@ -1,250 +0,0 @@ -#ifndef VERTEXCFD_UTILS_SMOOTHMATH_HPP -#define VERTEXCFD_UTILS_SMOOTHMATH_HPP - -#include "VertexCFD_Utils_Constants.hpp" -#include "VertexCFD_Utils_TypeTraits.hpp" - -#include - -#include - -namespace VertexCFD -{ -namespace SmoothMath -{ -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType abs(const T& x, const double tol) -{ - if (x >= tol) - { - return x; - } - else if (x <= -tol) - { - return -x; - } - else - { - return 0.5 * (x * x / tol + tol); - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -max(const T1& x, const T2& y, const double tol) -{ - if (tol == 0.0) - { - if (x < y) - { - return y; - } - else - { - return x; - } - } - else - { - return 0.5 * (x + y + abs(x - y, tol)); - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -min(const T1& x, const T2& y, const double tol) -{ - if (tol == 0.0) - { - if (x > y) - { - return y; - } - else - { - return x; - } - } - else - { - return 0.5 * (x + y - abs(x - y, tol)); - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -clamp(const T1& x, const T2& lo, const T3& hi, const double tol) -{ - // Note: This could be implemented as min(max(x, lo, tol), hi, tol) or - // similar, but that would result in more temporary/intermediate values - // being allocated. - - // -inf < x < inf - if (x <= lo - tol) - { - return lo; - } - - // lo - tol < x < inf - else if (x >= hi + tol) - { - return hi; - } - - // lo - tol < x < hi + tol - else if (x < lo + tol) - { - // lo - tol < x < lo + tol => -tol < (x-lo) < tol - return 0.5 * (x + lo + 0.5 * ((x - lo) * (x - lo) / tol + tol)); - } - - // lo + tol <= x < hi + tol - else if (x > hi - tol) - { - // hi - tol < x < hi + tol => -tol < (x-hi) < tol - return 0.5 * (x + hi - 0.5 * ((x - hi) * (x - hi) / tol + tol)); - } - - // lo + tol <= x <= h - tol - else - { - return x; - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -ramp(const T1& x, const T2& start, const T3& end) -{ - using std::sin; - constexpr double half_pi = 0.5 * Constants::pi; - if (x <= start) - { - return 0.0; - } - else if (x >= end) - { - return 1.0; - } - else - { - return 0.5 - * (sin(half_pi * (2.0 * x - (start + end)) / (end - start)) - + 1.0); - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -hypot(const T1& x, const T2& y, const double tol) -{ - using std::sqrt; - - const ResultType dotp = x * x + y * y; - const double tol2 = tol * tol; - - if (tol2 <= dotp) - { - return sqrt(dotp); - } - else - { - return 0.5 * (dotp + tol2) / tol; - } -} - -//---------------------------------------------------------------------------// -template -KOKKOS_INLINE_FUNCTION ResultType -hypot(const T1& x, const T2& y, const T3& z, const double tol) -{ - using std::sqrt; - - const ResultType dotp = x * x + y * y + z * z; - const double tol2 = tol * tol; - - if (tol2 <= dotp) - { - return sqrt(dotp); - } - else - { - return 0.5 * (dotp + tol2) / tol; - } -} - -template> -KOKKOS_INLINE_FUNCTION ReturnType norm(const T1& v, const double tol) -{ - using std::sqrt; - - using scalar_type = std::remove_cv_t; - scalar_type dotp = 0.0; - int num_space_dim = v.size(); - - for (int i = 0; i < num_space_dim; ++i) - { - dotp += v[i] * v[i]; - } - - const double tol2 = tol * tol; - if (tol2 <= dotp) - { - return sqrt(dotp); - } - else - { - return 0.5 * (dotp + tol2) / tol; - } -} - -//---------------------------------------------------------------------------// -template> -KOKKOS_INLINE_FUNCTION ReturnType norm(const T1& v, - const T2& M, - const double tol) -{ - using std::sqrt; - - using scalar_type = std::remove_cv_t; - scalar_type xi = 0.0; - scalar_type row_sum; - - int num_space_dim = M.extent(0); - - for (int i = 0; i < num_space_dim; ++i) - { - row_sum = 0.0; - for (int j = 0; j < num_space_dim; ++j) - { - row_sum += M(i, j) * v[j]; - } - xi += v[i] * row_sum; - } - - const double tol2 = tol * tol; - if (tol2 <= xi) - { - return sqrt(xi); - } - else - { - return 0.5 * (xi + tol2) / tol; - } -} - -//---------------------------------------------------------------------------// - -} // end namespace SmoothMath -} // end namespace VertexCFD - -#endif // end VERTEXCFD_UTILS_SMOOTHMATH_HPP diff --git a/src/utils/VertexCFD_Utils_TypeTraits.hpp b/src/utils/VertexCFD_Utils_TypeTraits.hpp deleted file mode 100644 index fa8c952..0000000 --- a/src/utils/VertexCFD_Utils_TypeTraits.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef VERTEXCFD_TYPETRAITS_HPP -#define VERTEXCFD_TYPETRAITS_HPP - -#include - -namespace VertexCFD -{ -namespace Utils -{ -namespace Impl -{ -//---------------------------------------------------------------------------// -// Get the resulting scalar type from a combination of one or more types which -// may be standard arithmetic types, AD scalars, or AD expression templates. -// -// This may be used as the return type of a generic function of one or more -// arguments and will ensure that a valid type is returned. -//---------------------------------------------------------------------------// -template -struct ResultType; - -//---------------------------------------------------------------------------// -// For a single type, forward to Sacado type trait to get the base type of an -// expression. -template -struct ResultType -{ - using type = typename Sacado::BaseExprType::type; -}; - -//---------------------------------------------------------------------------// -// For two types, forward to Sacado type trait for the promoted type of a -// binary operation. -template -struct ResultType -{ - using type = typename Sacado::Promote::type; -}; - -//---------------------------------------------------------------------------// -// For more than two types, apply the above recursively to get a single result -// type. -template -struct ResultType -{ - using type = - typename ResultType::type, - Types...>::type; -}; -//---------------------------------------------------------------------------// -} // namespace Impl -} // namespace Utils - -//---------------------------------------------------------------------------// -// User-facing type alias for all of the above. -// Since the primary use case is for return types of generic functions, add -// this alias to the VertexCFD namespace to avoid unnecessary verbosity. -template -using ResultType = typename Utils::Impl::ResultType::type; - -} // namespace VertexCFD - -#endif diff --git a/src/utils/VertexCFD_Utils_VectorField.hpp b/src/utils/VertexCFD_Utils_VectorField.hpp deleted file mode 100644 index 3f8c891..0000000 --- a/src/utils/VertexCFD_Utils_VectorField.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef VERTEXCFD_UTILS_VECTORFIELD_HPP -#define VERTEXCFD_UTILS_VECTORFIELD_HPP - -#include - -#include -#include -#include - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Utils -{ -//---------------------------------------------------------------------------// -// This function registers an evaluated Kokkos Array and also handles the -// special cases of initial conditions ('ics_data = true'). -template -void addEvaluatedVectorField( - PHX::EvaluatorWithBaseImpl& f, - const Teuchos::RCP& data_layout, - Kokkos::Array, NumSpaceDim>& kokkos_array, - const std::string& scalar_name, - const bool unshared = false) -{ - constexpr size_t num_space_dim = NumSpaceDim; - - for (size_t dim = 0; dim < num_space_dim; ++dim) - { - std::string name = scalar_name + std::to_string(dim); - kokkos_array[dim] - = PHX::MDField(name, data_layout); - f.addEvaluatedField(kokkos_array[dim]); - if (unshared) - f.addUnsharedField(kokkos_array[dim].fieldTag().clone()); - } -} - -//---------------------------------------------------------------------------// -// This function registers a contributed Kokkos Array -template -void addContributedVectorField( - PHX::EvaluatorWithBaseImpl& f, - const Teuchos::RCP& data_layout, - Kokkos::Array, NumSpaceDim>& kokkos_array, - const std::string& scalar_name) -{ - constexpr size_t num_space_dim = NumSpaceDim; - - for (size_t dim = 0; dim < num_space_dim; ++dim) - { - std::string name = scalar_name + std::to_string(dim); - kokkos_array[dim] - = PHX::MDField(name, data_layout); - f.addContributedField(kokkos_array[dim]); - } -} - -//---------------------------------------------------------------------------// -// This function registers a dependent Kokkos Array -template -void addDependentVectorField( - PHX::EvaluatorWithBaseImpl& f, - const Teuchos::RCP& data_layout, - Kokkos::Array, NumSpaceDim>& - kokkos_array, - const std::string& scalar_name) -{ - constexpr size_t num_space_dim = NumSpaceDim; - - for (size_t dim = 0; dim < num_space_dim; ++dim) - { - std::string name = scalar_name + std::to_string(dim); - kokkos_array[dim] - = PHX::MDField(name, data_layout); - f.addDependentField(kokkos_array[dim]); - } -} - -//---------------------------------------------------------------------------// - -} // end namespace Utils -} // end namespace VertexCFD - -#endif // end VERTEXCFD_UTILS_VECTORFIELD_HPP diff --git a/src/utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp b/src/utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp deleted file mode 100644 index f8d3f7d..0000000 --- a/src/utils/VertexCFD_Utils_VectorizeOutputFieldNames.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef VERTEXCFD_UTILS_VECTORIZEOUTPUTFIELDNAMES_HPP -#define VERTEXCFD_UTILS_VECTORIZEOUTPUTFIELDNAMES_HPP - -#include -#include - -#include - -namespace VertexCFD -{ -namespace VectorizeOutputFieldNames -{ -inline std::vector tokenizeParameter( - const Teuchos::StringIndexedOrderedValueObjectContainerBase::KeyObjectPair< - Teuchos::ParameterEntry> p) -{ - const auto& fields = Teuchos::any_cast(p.second.getAny()); - std::vector tokens; - panzer::StringTokenizer(tokens, fields, ",", true); - return tokens; -} - -inline void getOutputFieldsByType(const Teuchos::ParameterList& params, - std::vector& fields) -{ - for (const auto& param : params) - { - const auto tokens = tokenizeParameter(param); - fields.insert(fields.end(), tokens.begin(), tokens.end()); - } -} - -inline void getOutputFields(const Teuchos::ParameterList& params, - std::vector& out_fields, - std::vector& out_vec_fields) -{ - // get the "Quantities" output names - getOutputFieldsByType(params.sublist("Nodal Quantities"), out_fields); - getOutputFieldsByType(params.sublist("Cell Quantities"), out_fields); - getOutputFieldsByType(params.sublist("Cell Average Quantities"), - out_fields); - // get rid of repeated names - std::set out_set(out_fields.begin(), out_fields.end()); - out_fields.assign(out_set.begin(), out_set.end()); - // do the same for vector outputs - getOutputFieldsByType(params.sublist("Cell Average Vectors"), - out_vec_fields); - std::set out_vec_set(out_vec_fields.begin(), - out_vec_fields.end()); - out_vec_fields.assign(out_vec_set.begin(), out_vec_set.end()); -} - -} // namespace VectorizeOutputFieldNames -} // namespace VertexCFD - -#endif // VERTEXCFD_UTILS_VECTORIZEOUTPUTFIELDNAMES_HPP diff --git a/src/utils/VertexCFD_Utils_VelocityDim.cpp b/src/utils/VertexCFD_Utils_VelocityDim.cpp deleted file mode 100644 index 7151a86..0000000 --- a/src/utils/VertexCFD_Utils_VelocityDim.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "VertexCFD_Utils_VelocityDim.hpp" - -namespace PHX -{ - -// Shortened version of tag when, e.g., printing DAGs -template<> -std::string print() -{ - return "Vel"; -} - -} // namespace PHX diff --git a/src/utils/VertexCFD_Utils_VelocityDim.hpp b/src/utils/VertexCFD_Utils_VelocityDim.hpp deleted file mode 100644 index a4c3e4a..0000000 --- a/src/utils/VertexCFD_Utils_VelocityDim.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef VERTEXCFD_UTILS_VELOCITYDIM_HPP -#define VERTEXCFD_UTILS_VELOCITYDIM_HPP - -#include - -namespace VertexCFD -{ - -// PHX tag denoting velocity dimension for an MDField -struct VelocityDim -{ -}; - -} // namespace VertexCFD - -namespace PHX -{ - -// Shortened version of tag for, e.g., printing DAG -template<> -std::string print(); - -} // namespace PHX - -// Register tag as PHX extent -PHX_IS_EXTENT(VertexCFD::VelocityDim) - -#endif // VERTEXCFD_UTILS_VELOCITYDIM_HPP diff --git a/src/utils/VertexCFD_Utils_VelocityLayout.cpp b/src/utils/VertexCFD_Utils_VelocityLayout.cpp deleted file mode 100644 index 4d3ddae..0000000 --- a/src/utils/VertexCFD_Utils_VelocityLayout.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "VertexCFD_Utils_VelocityLayout.hpp" -#include "VertexCFD_Utils_VelocityDim.hpp" - -#include - -#include - -#include - -namespace VertexCFD -{ -namespace Utils -{ -Teuchos::RCP -buildVelocityLayout(const Teuchos::RCP& scalar_layout, - int num_vel_dims) -{ - return Teuchos::rcp( - new PHX::MDALayout( - scalar_layout->extent(0), scalar_layout->extent(1), num_vel_dims)); -} - -Teuchos::RCP buildVelocityGradLayout( - const Teuchos::RCP& vector_layout, int num_vel_dims) -{ - return Teuchos::rcp( - new PHX::MDALayout( - vector_layout->extent(0), - vector_layout->extent(1), - vector_layout->extent(2), - num_vel_dims)); -} - -} // namespace Utils -} // namespace VertexCFD diff --git a/src/utils/VertexCFD_Utils_VelocityLayout.hpp b/src/utils/VertexCFD_Utils_VelocityLayout.hpp deleted file mode 100644 index 0d3558a..0000000 --- a/src/utils/VertexCFD_Utils_VelocityLayout.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef VERTEXCFD_UTILS_VELOCITYLAYOUT_HPP -#define VERTEXCFD_UTILS_VELOCITYLAYOUT_HPP - -#include - -namespace VertexCFD -{ -namespace Utils -{ -// Build phalanx DataLayout corresponding to vector velocity field -Teuchos::RCP -buildVelocityLayout(const Teuchos::RCP& scalar_layout, - int num_vel_dims); - -// Build phalanx DataLayout corresponding to vector velocity gradient field -Teuchos::RCP -buildVelocityGradLayout(const Teuchos::RCP& vector_layout, - int num_vel_dims); -} // namespace Utils -} // namespace VertexCFD - -#endif // VERTEXCFD_UTILS_VELOCITYLAYOUT_HPP diff --git a/src/utils/VertexCFD_Utils_Version.cpp b/src/utils/VertexCFD_Utils_Version.cpp deleted file mode 100644 index 0c76fb7..0000000 --- a/src/utils/VertexCFD_Utils_Version.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -namespace VertexCFD -{ -namespace Utils -{ -std::string version() -{ - return VertexCFD_VERSION_STRING; -} - -std::string git_commit_hash() -{ - return VertexCFD_GIT_COMMIT_HASH; -} - -} // end namespace Utils -} // end namespace VertexCFD diff --git a/src/utils/VertexCFD_Utils_Version.hpp b/src/utils/VertexCFD_Utils_Version.hpp deleted file mode 100644 index 6a92dac..0000000 --- a/src/utils/VertexCFD_Utils_Version.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef VERTEXCFD_VERSION_HPP -#define VERTEXCFD_VERSION_HPP - -#include - -#include - -namespace VertexCFD -{ -namespace Utils -{ -std::string version(); - -std::string git_commit_hash(); - -} // end namespace Utils -} // end namespace VertexCFD - -#endif // end VERTEXCFD_VERSION_HPP diff --git a/src/utils/VertexCFD_Utils_config.hpp.cmakein b/src/utils/VertexCFD_Utils_config.hpp.cmakein deleted file mode 100644 index 58c9dc3..0000000 --- a/src/utils/VertexCFD_Utils_config.hpp.cmakein +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef VERTEXCFD_CORE_CONFIG_HPP -#define VERTEXCFD_CORE_CONFIG_HPP - -#define VertexCFD_VERSION_STRING "@PROJECT_VERSION@" - -#define VertexCFD_GIT_COMMIT_HASH "@VertexCFD_GIT_COMMIT_HASH@" - -#endif // VERTEXCFD_CORE_CONFIG_HPP diff --git a/src/utils/unit_test/CMakeLists.txt b/src/utils/unit_test/CMakeLists.txt deleted file mode 100644 index b897ac1..0000000 --- a/src/utils/unit_test/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -set(TEST_HARNESS_DIR ${CMAKE_SOURCE_DIR}/src/test_harness) -include(${TEST_HARNESS_DIR}/TestHarness.cmake) - -include_directories(${Trilinos_INCLUDE_DIRS} ${Trilinos_TPL_INCLUDE_DIRS}) -link_directories(${Trilinos_LIBRARY_DIRS} ${Trilinos_TPL_LIBRARY_DIRS}) - -VertexCFD_add_tests( - LIBS Utils ${Trilinos_LIBRARIES} ${Trilinos_TPL_LIBRARIES} - NAMES - Version - ParameterPack - SmoothMath - Constants - TypeTraits - MatrixMath - NonlinearSolver - ScalarToVector - VectorizeOutputFieldNames - ) diff --git a/src/utils/unit_test/doc/matrixMathReference.nb b/src/utils/unit_test/doc/matrixMathReference.nb deleted file mode 100644 index 7373b7b..0000000 --- a/src/utils/unit_test/doc/matrixMathReference.nb +++ /dev/null @@ -1,1173 +0,0 @@ -(* Content-type: application/vnd.wolfram.mathematica *) - -(*** Wolfram Notebook File ***) -(* http://www.wolfram.com/nb *) - -(* CreatedBy='Mathematica 12.1' *) - -(*CacheID: 234*) -(* Internal cache information: -NotebookFileLineBreakTest -NotebookFileLineBreakTest -NotebookDataPosition[ 158, 7] -NotebookDataLength[ 43604, 1163] -NotebookOptionsPosition[ 37169, 1060] -NotebookOutlinePosition[ 37564, 1076] -CellTagsIndexPosition[ 37521, 1073] -WindowFrame->Normal*) - -(* Beginning of Notebook Content *) -Notebook[{ -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - "3", "x3", " ", "LUP", " ", "decomp", " ", "and", " ", "solve", " ", - "test"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873885913334178*^9, 3.873885926222415*^9}}, - CellLabel-> - "In[786]:=",ExpressionUUID->"41e5b97b-b44f-4d15-87e6-9e053be85385"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"A", "=", - RowBox[{"{", - RowBox[{ - RowBox[{"{", - RowBox[{"1.", ",", "2.", ",", "4."}], "}"}], ",", - RowBox[{"{", - RowBox[{"2.", ",", "1.", ",", "3."}], "}"}], ",", - RowBox[{"{", - RowBox[{"3.", ",", "2.", ",", "4."}], "}"}]}], "}"}]}], ";"}]], "Input",\ - - CellChangeTimes->{{3.873548257732329*^9, 3.873548316656966*^9}, { - 3.873548686073326*^9, 3.873548691745556*^9}, 3.873818660415629*^9, { - 3.873885910099312*^9, 3.873885911371307*^9}}, - CellLabel-> - "In[787]:=",ExpressionUUID->"6f7b8d72-9554-4028-aa5b-4e447c8682f1"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"A", "//", "MatrixForm"}]], "Input", - CellChangeTimes->{{3.873548312742869*^9, 3.873548321091855*^9}, { - 3.873548395043783*^9, 3.873548403457623*^9}}, - CellLabel-> - "In[788]:=",ExpressionUUID->"f25673d5-c4ab-4d90-aff9-c1075f28a925"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"1.`", "2.`", "4.`"}, - {"2.`", "1.`", "3.`"}, - {"3.`", "2.`", "4.`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.873548321640725*^9, 3.873548398615109*^9, 3.873548693965193*^9, - 3.873818661864649*^9, 3.873818967095649*^9, 3.873820008326004*^9, - 3.873821529573874*^9, 3.8738215867307243`*^9, {3.87382162895057*^9, - 3.873821656354196*^9}, {3.873884591039816*^9, 3.873884609562522*^9}, - 3.87388487819849*^9, 3.873884937264092*^9, 3.873885017507765*^9, - 3.873885503681518*^9, 3.8738858536640673`*^9, 3.873885891832185*^9, - 3.873886061371482*^9, 3.8738862162039413`*^9, 3.8738863039216013`*^9, - 3.8738863550992813`*^9, 3.873886414516436*^9}, - CellLabel-> - "Out[788]//MatrixForm=",ExpressionUUID->"fa8a9d60-b48d-43ea-9780-\ -80209e904b9e"] -}, Open ]], - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"lu", ",", "p", ",", "c"}], "}"}], "=", - RowBox[{"LUDecomposition", "[", "A", "]"}]}], ";"}]], "Input", - CellChangeTimes->{{3.873548323225016*^9, 3.873548359127983*^9}, - 3.8735484063800783`*^9, 3.873818667849622*^9}, - CellLabel-> - "In[789]:=",ExpressionUUID->"2b6cf90a-ed7e-46ba-9cbd-97ff2db95ff9"], - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{"lu", "//", "MatrixForm"}], "//", "Rationalize"}], ";"}]], "Input",\ - - CellChangeTimes->{{3.87354840844627*^9, 3.873548413639083*^9}, { - 3.873548701567439*^9, 3.873548703993381*^9}, 3.873885497823937*^9}, - CellLabel-> - "In[790]:=",ExpressionUUID->"817188e7-589a-4e7e-94fe-fa8020639d81"], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - "expected", " ", "permutation", " ", "p", " ", "corrected", " ", "for", " ", - RowBox[{"C", "++"}], " ", "indexing"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873884884865815*^9, 3.873884909733581*^9}}, - CellLabel-> - "In[791]:=",ExpressionUUID->"21cbb4e7-9a74-4bfb-b1d4-55ae75260b4d"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"expectedP", "=", - RowBox[{"p", "-", "1"}]}]], "Input", - CellChangeTimes->{ - 3.873549339177335*^9, {3.873818413511498*^9, 3.87381841358959*^9}, { - 3.87388491241017*^9, 3.873884922387897*^9}}, - CellLabel-> - "In[792]:=",ExpressionUUID->"b2739b32-3614-4b53-89be-12abf10d241c"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"2", ",", "0", ",", "1"}], "}"}]], "Output", - CellChangeTimes->{ - 3.873549339437284*^9, 3.873818413929639*^9, 3.8738186704958553`*^9, - 3.873818968912184*^9, 3.873820008380271*^9, 3.873821529618164*^9, - 3.8738215867945127`*^9, {3.873821629020382*^9, 3.87382165639649*^9}, { - 3.873884591085081*^9, 3.873884609578476*^9}, 3.873884878246854*^9, { - 3.8738849247943974`*^9, 3.8738849373147793`*^9}, 3.8738850175649843`*^9, - 3.873885503785583*^9, 3.8738858537433968`*^9, 3.87388589189185*^9, - 3.873886061427105*^9, 3.873886216267989*^9, 3.873886303981415*^9, - 3.873886355155985*^9, 3.873886414566762*^9}, - CellLabel-> - "Out[792]=",ExpressionUUID->"b71f7dcd-4d67-47e6-b472-015d133fb489"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - RowBox[{ - "In", " ", "VertexCFD", " ", "the", " ", "LU", " ", "matrix", " ", "is", - " ", "stored", " ", "\"\\""}], ",", " ", - RowBox[{ - RowBox[{ - "the", " ", "expected", " ", "LU", " ", "matrix", " ", "should", " ", - "be", " ", "such", " ", "that", " ", - RowBox[{"A", "[", - RowBox[{"[", "p", "]"}], "]"}]}], " ", "=", " ", - RowBox[{ - "LU", " ", "where", " ", "LU", " ", "is", " ", "the", " ", "returned", - " ", "matrix", " ", "from", " ", "Mathematica", " ", - "LUDecomposition"}]}]}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873884702618126*^9, 3.8738848352981443`*^9}}, - CellLabel-> - "In[793]:=",ExpressionUUID->"9dec25d4-6691-4f80-95b6-6d31800fcc15"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"pprime", "=", - RowBox[{"{", - RowBox[{"2", ",", "3", ",", "1"}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.8735493429708643`*^9, 3.8735493916229753`*^9}, { - 3.873818359166994*^9, 3.8738183641551657`*^9}, 3.873818675095749*^9}, - CellLabel-> - "In[794]:=",ExpressionUUID->"d8e98558-a838-471f-891b-f5fde99a5806"], - -Cell[CellGroupData[{ - -Cell[BoxData[{ - RowBox[{ - RowBox[{"expectedA", "=", - RowBox[{"lu", "[", - RowBox[{"[", "pprime", "]"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"expectedA", "//", "MatrixForm"}], "//", - "Rationalize"}], "\[IndentingNewLine]", - RowBox[{"expectedA", "//", "Rationalize"}]}], "Input", - CellChangeTimes->{{3.873549355466848*^9, 3.8735493655874434`*^9}, { - 3.8738215444937468`*^9, 3.873821548219389*^9}, {3.8738848388709*^9, - 3.8738848727353697`*^9}, 3.873885845159299*^9, {3.873885879713991*^9, - 3.873885887174007*^9}}, - CellLabel-> - "In[795]:=",ExpressionUUID->"9daa7836-803d-4fec-b35e-e414268c5517"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - { - FractionBox["1", "3"], - FractionBox["4", "3"], - FractionBox["8", "3"]}, - { - FractionBox["2", "3"], - RowBox[{"-", - FractionBox["1", "4"]}], "1"}, - {"3", "2", "4"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{{3.873549358307843*^9, 3.87354939471743*^9}, { - 3.873818353691806*^9, 3.8738183657065277`*^9}, 3.873818676495591*^9, - 3.873818970136085*^9, 3.873820008420864*^9, 3.8738215296558943`*^9, - 3.873821586832116*^9, {3.873821629055581*^9, 3.873821656433942*^9}, { - 3.873884591123021*^9, 3.873884609629637*^9}, {3.873884867415019*^9, - 3.873884878292452*^9}, 3.873884937355174*^9, 3.87388501761064*^9, - 3.873885503860903*^9, {3.873885845677463*^9, 3.873885853817615*^9}, { - 3.8738858875973797`*^9, 3.87388589194584*^9}, 3.873886061471549*^9, - 3.873886216323769*^9, 3.873886304030122*^9, 3.873886355168932*^9, - 3.873886414609803*^9}, - CellLabel-> - "Out[796]//MatrixForm=",ExpressionUUID->"87fe9717-8fd0-4878-8162-\ -f397c94a16ec"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{ - RowBox[{"{", - RowBox[{ - FractionBox["1", "3"], ",", - FractionBox["4", "3"], ",", - FractionBox["8", "3"]}], "}"}], ",", - RowBox[{"{", - RowBox[{ - FractionBox["2", "3"], ",", - RowBox[{"-", - FractionBox["1", "4"]}], ",", "1"}], "}"}], ",", - RowBox[{"{", - RowBox[{"3", ",", "2", ",", "4"}], "}"}]}], "}"}]], "Output", - CellChangeTimes->{{3.873549358307843*^9, 3.87354939471743*^9}, { - 3.873818353691806*^9, 3.8738183657065277`*^9}, 3.873818676495591*^9, - 3.873818970136085*^9, 3.873820008420864*^9, 3.8738215296558943`*^9, - 3.873821586832116*^9, {3.873821629055581*^9, 3.873821656433942*^9}, { - 3.873884591123021*^9, 3.873884609629637*^9}, {3.873884867415019*^9, - 3.873884878292452*^9}, 3.873884937355174*^9, 3.87388501761064*^9, - 3.873885503860903*^9, {3.873885845677463*^9, 3.873885853817615*^9}, { - 3.8738858875973797`*^9, 3.87388589194584*^9}, 3.873886061471549*^9, - 3.873886216323769*^9, 3.873886304030122*^9, 3.873886355168932*^9, - 3.8738864146118107`*^9}, - CellLabel-> - "Out[797]=",ExpressionUUID->"309e5573-4259-4baa-9368-d59ec2c596b6"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - "check", " ", "that", " ", "we", " ", "actually", " ", "get", " ", "lu", - " ", "using", " ", "p", " ", "as", " ", "intended"}], " ", "*)"}]], "Input",\ - - CellChangeTimes->{{3.873884984255637*^9, 3.873885009887908*^9}, { - 3.873885943767289*^9, 3.873885945720776*^9}}, - CellLabel-> - "In[798]:=",ExpressionUUID->"4a2871bb-acdf-4f0e-b33b-fe9c6e0d6f2b"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"expectedA", "[", - RowBox[{"[", "p", "]"}], "]"}], "-", "lu"}], "//", "MatrixForm"}], "//", - "Rationalize"}]], "Input", - CellChangeTimes->{{3.873884566989442*^9, 3.8738846054651623`*^9}, { - 3.873884859023572*^9, 3.873884861111637*^9}, {3.8738850121923523`*^9, - 3.873885013069188*^9}}, - CellLabel-> - "In[799]:=",ExpressionUUID->"4a9e265f-de0c-48aa-a0ef-6f9c598e5c51"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"0", "0", "0"}, - {"0", "0", "0"}, - {"0", "0", "0"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{{3.8738845852851887`*^9, 3.8738846096381693`*^9}, - 3.8738848782984962`*^9, 3.873884937360505*^9, {3.873885013677095*^9, - 3.873885017620503*^9}, 3.8738855039346523`*^9, 3.8738858539533*^9, - 3.873885892010192*^9, 3.8738860614812603`*^9, 3.873886216376768*^9, - 3.873886304077029*^9, 3.873886355208338*^9, 3.8738864146197853`*^9}, - CellLabel-> - "Out[799]//MatrixForm=",ExpressionUUID->"4db56fae-df15-40dc-82dc-\ -cea0d9768988"] -}, Open ]], - -Cell[BoxData[ - RowBox[{ - RowBox[{"l", "=", - RowBox[{ - RowBox[{"lu", " ", - RowBox[{"SparseArray", "[", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"i_", ",", "j_"}], "}"}], "/;", - RowBox[{"j", "<", "i"}]}], "\[Rule]", "1"}], ",", - RowBox[{"{", - RowBox[{"3", ",", "3"}], "}"}]}], "]"}]}], "+", - RowBox[{"IdentityMatrix", "[", "3", "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8738189531514263`*^9, 3.873818957142659*^9}, { - 3.873821490692011*^9, 3.873821492113802*^9}}, - CellLabel-> - "In[800]:=",ExpressionUUID->"bdab867a-b5a2-4fe0-836c-9bd36622b9ec"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"u", "=", - RowBox[{"lu", " ", - RowBox[{"SparseArray", "[", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"i_", ",", "j_"}], "}"}], "/;", - RowBox[{"j", "\[GreaterEqual]", "i"}]}], "\[Rule]", "1"}], ",", - RowBox[{"{", - RowBox[{"3", ",", "3"}], "}"}]}], "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8738189921521378`*^9, 3.8738189933210783`*^9}, { - 3.8738214996845427`*^9, 3.873821504802157*^9}}, - CellLabel-> - "In[801]:=",ExpressionUUID->"d9347773-9ae3-4df3-a691-e0a3bf335ca7"], - -Cell[CellGroupData[{ - -Cell[BoxData[{ - RowBox[{"MatrixForm", "[", "l", "]"}], "\[IndentingNewLine]", - RowBox[{"MatrixForm", "[", "u", "]"}], "\[IndentingNewLine]", - RowBox[{"MatrixForm", "[", - RowBox[{"l", ".", "u"}], "]"}]}], "Input", - CellChangeTimes->{{3.87381899900133*^9, 3.873819046537306*^9}, { - 3.873821521707954*^9, 3.873821525857964*^9}}, - CellLabel-> - "In[802]:=",ExpressionUUID->"67ab2db9-006a-4dbc-918c-f1595187f78b"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"1", "0", "0"}, - {"0.3333333333333333`", "1", "0"}, - {"0.6666666666666666`", - RowBox[{"-", "0.24999999999999992`"}], "1"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.87382158687075*^9, {3.873821629069867*^9, 3.873821656475794*^9}, { - 3.87388459116961*^9, 3.87388460968709*^9}, 3.873884878342246*^9, - 3.873884937402584*^9, 3.873885017662006*^9, 3.87388550399695*^9, - 3.873885854027176*^9, 3.8738858920689287`*^9, 3.8738860615273037`*^9, - 3.873886216424542*^9, 3.8738863041322412`*^9, 3.873886355252397*^9, - 3.873886414659128*^9}, - CellLabel-> - "Out[802]//MatrixForm=",ExpressionUUID->"e91bb598-3e5b-42c7-8e0e-\ -6eb46d06d4c5"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"3.`", "2.`", "4.`"}, - {"0", "1.3333333333333335`", "2.666666666666667`"}, - {"0", "0", "1.`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[ - SparseArray[ - Automatic, {3, 3}, 0, { - 1, {{0, 3, 5, 6}, {{3}, {2}, {1}, {2}, {3}, {3}}}, {4., 2., 3., - 1.3333333333333335`, 2.666666666666667, 1.}}]]]]], "Output", - CellChangeTimes->{ - 3.87382158687075*^9, {3.873821629069867*^9, 3.873821656475794*^9}, { - 3.87388459116961*^9, 3.87388460968709*^9}, 3.873884878342246*^9, - 3.873884937402584*^9, 3.873885017662006*^9, 3.87388550399695*^9, - 3.873885854027176*^9, 3.8738858920689287`*^9, 3.8738860615273037`*^9, - 3.873886216424542*^9, 3.8738863041322412`*^9, 3.873886355252397*^9, - 3.87388641466195*^9}, - CellLabel-> - "Out[803]//MatrixForm=",ExpressionUUID->"7797d898-12b5-4f05-bfd8-\ -9eaa69004166"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"3.`", "2.`", "4.`"}, - {"1.`", "2.`", "4.`"}, - {"2.`", "1.`", "3.`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.87382158687075*^9, {3.873821629069867*^9, 3.873821656475794*^9}, { - 3.87388459116961*^9, 3.87388460968709*^9}, 3.873884878342246*^9, - 3.873884937402584*^9, 3.873885017662006*^9, 3.87388550399695*^9, - 3.873885854027176*^9, 3.8738858920689287`*^9, 3.8738860615273037`*^9, - 3.873886216424542*^9, 3.8738863041322412`*^9, 3.873886355252397*^9, - 3.8738864146638193`*^9}, - CellLabel-> - "Out[804]//MatrixForm=",ExpressionUUID->"ac97130c-200c-423e-834b-\ -be1bfb97047a"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{"check", " ", "solve", " ", "step"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873886029840823*^9, 3.8738860371994677`*^9}}, - CellLabel-> - "In[805]:=",ExpressionUUID->"bfdf5b61-e9bf-45ca-8335-c223dda8f4d6"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"b", "=", - RowBox[{"{", - RowBox[{"3", ",", "4", ",", "5"}], "}"}]}]], "Input", - CellChangeTimes->{{3.8738190651879673`*^9, 3.8738190803876038`*^9}, { - 3.8738193748964863`*^9, 3.87381937632789*^9}}, - CellLabel-> - "In[806]:=",ExpressionUUID->"5f62f4af-3da9-4418-a89f-9e9ed2034ecb"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"3", ",", "4", ",", "5"}], "}"}]], "Output", - CellChangeTimes->{ - 3.873819377211627*^9, 3.873820008469236*^9, 3.873821529716867*^9, - 3.8738215869514837`*^9, {3.873821629160358*^9, 3.873821656559039*^9}, { - 3.873884591224436*^9, 3.873884609777454*^9}, 3.873884878431637*^9, - 3.87388493748067*^9, 3.8738850177162857`*^9, 3.873885504098394*^9, - 3.873885854113805*^9, 3.873885892164158*^9, 3.873886061576597*^9, - 3.873886216470271*^9, 3.873886304185693*^9, 3.873886355299405*^9, - 3.873886414700492*^9}, - CellLabel-> - "Out[806]=",ExpressionUUID->"7a5d7922-5f93-4aa7-ada6-b022923f8461"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{"using", " ", "LU", " ", "decomposition"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.87388608555926*^9, 3.873886093298985*^9}}, - CellLabel-> - "In[807]:=",ExpressionUUID->"99477342-5c67-45ac-8133-c92237cd5832"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"y", "=", - RowBox[{"LinearSolve", "[", - RowBox[{"l", ",", - RowBox[{"b", "[", - RowBox[{"[", "p", "]"}], "]"}]}], "]"}]}], ";"}]], "Input", - CellChangeTimes->{{3.873819384901676*^9, 3.873819398114263*^9}, { - 3.873819989085167*^9, 3.873819995521783*^9}}, - CellLabel-> - "In[808]:=",ExpressionUUID->"282f3c34-4b37-4864-bb1c-1e55d0557afe"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"LinearSolve", "[", - RowBox[{"u", ",", "y"}], "]"}]], "Input", - CellChangeTimes->{{3.873819404323048*^9, 3.873819410009388*^9}}, - CellLabel-> - "In[809]:=",ExpressionUUID->"2e9bafd5-c3f0-4f20-ada5-c244e9da15df"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"1.0000000000000002`", ",", - RowBox[{"-", "1.`"}], ",", "1.`"}], "}"}]], "Output", - CellChangeTimes->{ - 3.8738194110686007`*^9, 3.8738200085132713`*^9, 3.8738215297524033`*^9, - 3.8738215869854307`*^9, {3.873821629171023*^9, 3.873821656598094*^9}, { - 3.873884591261627*^9, 3.873884609827282*^9}, 3.8738848784765863`*^9, - 3.873884937515682*^9, 3.873885017755444*^9, 3.873885504148225*^9, - 3.87388585416967*^9, 3.873885892174415*^9, 3.8738860616237373`*^9, - 3.873886216515822*^9, 3.873886304242941*^9, 3.873886355345083*^9, - 3.873886414741547*^9}, - CellLabel-> - "Out[809]=",ExpressionUUID->"9a17b002-ad5e-4b6c-908a-ca6093153154"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - "let", " ", "Mathematica", " ", "solve", " ", "the", " ", "original", " ", - "system", " ", "as", " ", "a", " ", "check"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873886095551296*^9, 3.8738861436355247`*^9}}, - CellLabel-> - "In[810]:=",ExpressionUUID->"ee22a100-a557-4a76-9309-a46c1731d8c7"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"LinearSolve", "[", - RowBox[{"A", ",", "b"}], "]"}]], "Input", - CellChangeTimes->{{3.8738860478725147`*^9, 3.873886056133404*^9}}, - CellLabel-> - "In[811]:=",ExpressionUUID->"cbca244e-4be6-4346-a7ab-dc5203e7bdef"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"1.`", ",", - RowBox[{"-", "1.`"}], ",", "1.`"}], "}"}]], "Output", - CellChangeTimes->{{3.873886057488387*^9, 3.8738860616310577`*^9}, - 3.873886216559017*^9, 3.873886304253804*^9, 3.873886355355495*^9, - 3.873886414750389*^9}, - CellLabel-> - "Out[811]=",ExpressionUUID->"32fb9c23-f868-4c65-80f6-6f046aff4e6e"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{"Test", " ", "5", "x5", " ", "decomposition", " ", "only"}], " ", - "*)"}]], "Input", - CellChangeTimes->{{3.873886170997048*^9, 3.873886178058332*^9}}, - CellLabel-> - "In[812]:=",ExpressionUUID->"343c9c1b-6f39-477f-8d94-059828fedb35"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"A", " ", "=", " ", - RowBox[{"{", - RowBox[{ - RowBox[{"{", - RowBox[{"8.", ",", - RowBox[{"-", "2."}], ",", - RowBox[{"-", "8."}], ",", "9.", ",", "6."}], "}"}], ",", - RowBox[{"{", - RowBox[{"9.", ",", "5.", ",", "5.", ",", "3.", ",", "1."}], "}"}], ",", - - RowBox[{"{", - RowBox[{"9.", ",", - RowBox[{"-", "7."}], ",", "2.", ",", "8.", ",", "4."}], "}"}], ",", - RowBox[{"{", - RowBox[{"9.", ",", "6.", ",", - RowBox[{"-", "9."}], ",", "0", ",", - RowBox[{"-", "8."}]}], "}"}], ",", - RowBox[{"{", - RowBox[{ - RowBox[{"-", "7."}], ",", "9.", ",", - RowBox[{"-", "9."}], ",", - RowBox[{"-", "7."}], ",", "6."}], "}"}]}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.8738184449190407`*^9, 3.8738184849828157`*^9}, { - 3.873818650526903*^9, 3.873818655517494*^9}, {3.873818686367817*^9, - 3.873818718008418*^9}}, - CellLabel-> - "In[813]:=",ExpressionUUID->"13a7c346-7eb1-46b0-86dc-035a5dfe3833"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"A", "//", "MatrixForm"}]], "Input", - CellChangeTimes->{{3.873818719049061*^9, 3.8738187218489656`*^9}}, - CellLabel-> - "In[814]:=",ExpressionUUID->"47a2829d-8e01-4bb0-893f-5b6e49dc6e33"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"8.`", - RowBox[{"-", "2.`"}], - RowBox[{"-", "8.`"}], "9.`", "6.`"}, - {"9.`", "5.`", "5.`", "3.`", "1.`"}, - {"9.`", - RowBox[{"-", "7.`"}], "2.`", "8.`", "4.`"}, - {"9.`", "6.`", - RowBox[{"-", "9.`"}], "0", - RowBox[{"-", "8.`"}]}, - { - RowBox[{"-", "7.`"}], "9.`", - RowBox[{"-", "9.`"}], - RowBox[{"-", "7.`"}], "6.`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.8738187222913523`*^9, 3.873820008554089*^9, 3.873821529787386*^9, - 3.873821587019372*^9, {3.873821629204485*^9, 3.873821656637444*^9}, { - 3.8738845912965937`*^9, 3.873884609869782*^9}, 3.873884878517249*^9, - 3.873884937550674*^9, 3.873885017794985*^9, 3.8738855041986103`*^9, - 3.873885854222453*^9, 3.873885892217064*^9, 3.873886061680459*^9, - 3.873886216608362*^9, 3.873886304320012*^9, 3.873886355401146*^9, - 3.873886414792509*^9}, - CellLabel-> - "Out[814]//MatrixForm=",ExpressionUUID->"8573a0d9-eb3d-4586-ba90-\ -761fef5af103"] -}, Open ]], - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"lu", ",", "p", ",", "c"}], "}"}], "=", - RowBox[{"LUDecomposition", "[", "A", "]"}]}], ";"}]], "Input", - CellLabel-> - "In[815]:=",ExpressionUUID->"37953d2b-014a-427a-887b-588a6aa10f2e"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"expectedP", "=", - RowBox[{"p", "-", "1"}]}]], "Input", - CellChangeTimes->{{3.873818760307156*^9, 3.873818760665497*^9}, { - 3.873886192387355*^9, 3.8738861948763523`*^9}}, - CellLabel-> - "In[816]:=",ExpressionUUID->"8d2e9e4e-96ba-42c1-9fc3-e845f655e08f"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"1", ",", "4", ",", "0", ",", "3", ",", "2"}], "}"}]], "Output", - CellChangeTimes->{ - 3.873818761738782*^9, 3.873820008604332*^9, 3.8738215298539057`*^9, - 3.8738215870345173`*^9, {3.873821629242901*^9, 3.873821656680029*^9}, { - 3.873884591344514*^9, 3.873884609941598*^9}, 3.873884878533403*^9, - 3.873884937597088*^9, 3.8738850178409567`*^9, 3.873885504302503*^9, - 3.873885854280108*^9, 3.873885892234123*^9, 3.873886061729907*^9, { - 3.873886195654173*^9, 3.873886216623321*^9}, 3.873886304334384*^9, - 3.873886355444621*^9, 3.8738864148015127`*^9}, - CellLabel-> - "Out[816]=",ExpressionUUID->"53329e55-30da-499d-949c-c7cfccc3b0f9"] -}, Open ]], - -Cell[BoxData[ - RowBox[{ - RowBox[{"pprime", "=", - RowBox[{"{", - RowBox[{"3", ",", "1", ",", "5", ",", "4", ",", "2"}], "}"}]}], - ";"}]], "Input", - CellChangeTimes->{{3.873818773706819*^9, 3.873818828939896*^9}}, - CellLabel-> - "In[817]:=",ExpressionUUID->"626d7270-4e75-4b49-a8a8-42895eda885f"], - -Cell[CellGroupData[{ - -Cell[BoxData[{ - RowBox[{ - RowBox[{"expectedA", "=", - RowBox[{"lu", "[", - RowBox[{"[", "pprime", "]"}], "]"}]}], ";"}], "\[IndentingNewLine]", - RowBox[{ - RowBox[{"expectedA", "//", "MatrixForm"}], "//", - "Rationalize"}], "\[IndentingNewLine]", - RowBox[{"expectedA", "//", "Rationalize"}]}], "Input", - CellChangeTimes->{{3.873818830563217*^9, 3.873818841051112*^9}, { - 3.873886188332423*^9, 3.873886228996368*^9}}, - CellLabel-> - "In[818]:=",ExpressionUUID->"dea06982-b0de-45c7-8f63-cc86c1ca456d"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - { - FractionBox["8", "9"], - RowBox[{"-", - FractionBox["1", "2"]}], - RowBox[{"-", "15"}], "4", - FractionBox["17", "2"]}, - {"9", "5", "5", "3", "1"}, - {"1", - RowBox[{"-", - FractionBox["27", "29"]}], - FractionBox["15", "29"], - FractionBox["410", "1817"], - FractionBox["31989", "3634"]}, - {"1", - FractionBox["9", "116"], - FractionBox["263", "290"], - RowBox[{"-", - FractionBox["1817", "290"]}], - RowBox[{"-", - FractionBox["2499", "145"]}]}, - { - RowBox[{"-", - FractionBox["7", "9"]}], - FractionBox["116", "9"], - RowBox[{"-", - FractionBox["46", "9"]}], - RowBox[{"-", - FractionBox["14", "3"]}], - FractionBox["61", "9"]} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.873818841845112*^9, 3.87382000864118*^9, 3.873821529888377*^9, - 3.873821587071231*^9, {3.8738216292755623`*^9, 3.873821656718169*^9}, { - 3.873884591354697*^9, 3.873884609978272*^9}, 3.87388487857213*^9, - 3.87388493763205*^9, 3.873885017850555*^9, 3.873885504361775*^9, - 3.8738858543366737`*^9, 3.873885892276359*^9, 3.87388606177212*^9, { - 3.8738862166654863`*^9, 3.873886229766782*^9}, 3.873886304380315*^9, - 3.873886355490202*^9, 3.873886414841305*^9}, - CellLabel-> - "Out[819]//MatrixForm=",ExpressionUUID->"660f4a7a-63f1-4d84-bc33-\ -76ef15c71a78"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{ - RowBox[{"{", - RowBox[{ - FractionBox["8", "9"], ",", - RowBox[{"-", - FractionBox["1", "2"]}], ",", - RowBox[{"-", "15"}], ",", "4", ",", - FractionBox["17", "2"]}], "}"}], ",", - RowBox[{"{", - RowBox[{"9", ",", "5", ",", "5", ",", "3", ",", "1"}], "}"}], ",", - RowBox[{"{", - RowBox[{"1", ",", - RowBox[{"-", - FractionBox["27", "29"]}], ",", - FractionBox["15", "29"], ",", - FractionBox["410", "1817"], ",", - FractionBox["31989", "3634"]}], "}"}], ",", - RowBox[{"{", - RowBox[{"1", ",", - FractionBox["9", "116"], ",", - FractionBox["263", "290"], ",", - RowBox[{"-", - FractionBox["1817", "290"]}], ",", - RowBox[{"-", - FractionBox["2499", "145"]}]}], "}"}], ",", - RowBox[{"{", - RowBox[{ - RowBox[{"-", - FractionBox["7", "9"]}], ",", - FractionBox["116", "9"], ",", - RowBox[{"-", - FractionBox["46", "9"]}], ",", - RowBox[{"-", - FractionBox["14", "3"]}], ",", - FractionBox["61", "9"]}], "}"}]}], "}"}]], "Output", - CellChangeTimes->{ - 3.873818841845112*^9, 3.87382000864118*^9, 3.873821529888377*^9, - 3.873821587071231*^9, {3.8738216292755623`*^9, 3.873821656718169*^9}, { - 3.873884591354697*^9, 3.873884609978272*^9}, 3.87388487857213*^9, - 3.87388493763205*^9, 3.873885017850555*^9, 3.873885504361775*^9, - 3.8738858543366737`*^9, 3.873885892276359*^9, 3.87388606177212*^9, { - 3.8738862166654863`*^9, 3.873886229766782*^9}, 3.873886304380315*^9, - 3.873886355490202*^9, 3.8738864148438883`*^9}, - CellLabel-> - "Out[820]=",ExpressionUUID->"eb717ef3-5ecf-4d7a-a71e-646410641d53"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{ - RowBox[{"check", " ", "that", " ", - RowBox[{"A_exp", "[", - RowBox[{"[", "p", "]"}], "]"}]}], " ", "=", " ", "lu"}], " ", - "*)"}]], "Input", - CellChangeTimes->{{3.87388636613663*^9, 3.873886377577084*^9}}, - CellLabel-> - "In[821]:=",ExpressionUUID->"4b28e487-55a0-44ce-8ff6-707135d41744"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{"expectedA", "[", - RowBox[{"[", "p", "]"}], "]"}], "-", "lu"}], "//", - "MatrixForm"}]], "Input", - CellChangeTimes->{{3.873886340344843*^9, 3.873886352132577*^9}}, - CellLabel-> - "In[822]:=",ExpressionUUID->"69ef9827-97bf-4f09-b8d0-d064d9d7e538"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - {"0.`", "0.`", "0.`", "0.`", "0.`"}, - {"0.`", "0.`", "0.`", "0.`", "0.`"}, - {"0.`", "0.`", "0.`", "0.`", "0.`"}, - {"0.`", "0.`", "0.`", "0.`", "0.`"}, - {"0.`", "0.`", "0.`", "0.`", "0.`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{3.873886355497346*^9, 3.873886414852344*^9}, - CellLabel-> - "Out[822]//MatrixForm=",ExpressionUUID->"c59573a8-ab3e-48ce-a02d-\ -ed13c441b61f"] -}, Open ]], - -Cell[BoxData[ - RowBox[{"(*", " ", - RowBox[{"Test", " ", "solve", " ", "5", "x5"}], " ", "*)"}]], "Input", - CellChangeTimes->{{3.873886384282916*^9, 3.873886399819332*^9}}, - CellLabel-> - "In[823]:=",ExpressionUUID->"79f92fc0-4767-4a7f-aa0c-6d097ea669c3"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"A", " ", "=", " ", - RowBox[{"{", - RowBox[{ - RowBox[{"{", - RowBox[{ - RowBox[{"-", "2.6"}], ",", - RowBox[{"-", "7.2"}], ",", - RowBox[{"-", "0.4"}], ",", "7.5", ",", "5.7"}], "}"}], ",", - RowBox[{"{", - RowBox[{"2.2", ",", - RowBox[{"-", "4.9"}], ",", "3.8", ",", - RowBox[{"-", "8.7"}], ",", - RowBox[{"-", "4.6"}]}], "}"}], ",", - RowBox[{"{", - RowBox[{"4.8", ",", - RowBox[{"-", "4.6"}], ",", "5.7", ",", "7.9", ",", - RowBox[{"-", "8.5"}]}], "}"}], ",", - RowBox[{"{", - RowBox[{ - RowBox[{"-", "4.5"}], ",", "1.4", ",", - RowBox[{"-", "9.5"}], ",", "2.7", ",", - RowBox[{"-", "7.2"}]}], "}"}], ",", - RowBox[{"{", - RowBox[{"2.1", ",", - RowBox[{"-", "2.6"}], ",", - RowBox[{"-", "7.4"}], ",", - RowBox[{"-", "5.3"}], ",", "0.3"}], "}"}]}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.873820234954137*^9, 3.8738203538483877`*^9}}, - CellLabel-> - "In[824]:=",ExpressionUUID->"7490cda9-db9e-4685-9141-0ea5557776e5"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"A", "//", "MatrixForm"}]], "Input", - CellChangeTimes->{{3.873820357471552*^9, 3.8738203599108152`*^9}}, - CellLabel-> - "In[825]:=",ExpressionUUID->"157fafe0-d722-4a26-b1f2-4dd55a612811"], - -Cell[BoxData[ - TagBox[ - RowBox[{"(", "\[NoBreak]", GridBox[{ - { - RowBox[{"-", "2.6`"}], - RowBox[{"-", "7.2`"}], - RowBox[{"-", "0.4`"}], "7.5`", "5.7`"}, - {"2.2`", - RowBox[{"-", "4.9`"}], "3.8`", - RowBox[{"-", "8.7`"}], - RowBox[{"-", "4.6`"}]}, - {"4.8`", - RowBox[{"-", "4.6`"}], "5.7`", "7.9`", - RowBox[{"-", "8.5`"}]}, - { - RowBox[{"-", "4.5`"}], "1.4`", - RowBox[{"-", "9.5`"}], "2.7`", - RowBox[{"-", "7.2`"}]}, - {"2.1`", - RowBox[{"-", "2.6`"}], - RowBox[{"-", "7.4`"}], - RowBox[{"-", "5.3`"}], "0.3`"} - }, - GridBoxAlignment->{"Columns" -> {{Center}}, "Rows" -> {{Baseline}}}, - GridBoxSpacings->{"Columns" -> { - Offset[0.27999999999999997`], { - Offset[0.7]}, - Offset[0.27999999999999997`]}, "Rows" -> { - Offset[0.2], { - Offset[0.4]}, - Offset[0.2]}}], "\[NoBreak]", ")"}], - Function[BoxForm`e$, - MatrixForm[BoxForm`e$]]]], "Output", - CellChangeTimes->{ - 3.873820360295783*^9, 3.8738215299203253`*^9, 3.873821587103825*^9, { - 3.873821629308641*^9, 3.8738216567526484`*^9}, {3.873884591390791*^9, - 3.8738846099871893`*^9}, 3.873884878581976*^9, 3.873884937665495*^9, - 3.873885017887765*^9, 3.873885504422566*^9, 3.8738858543995*^9, - 3.873885892321146*^9, 3.873886061814756*^9, 3.873886216705024*^9, - 3.873886304421962*^9, 3.873886355539104*^9, 3.8738864148965883`*^9}, - CellLabel-> - "Out[825]//MatrixForm=",ExpressionUUID->"14cc1f82-a6e4-4307-ad3d-\ -a53257cb251e"] -}, Open ]], - -Cell[BoxData[ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"lu", ",", "p", ",", "c"}], "}"}], "=", - RowBox[{"LUDecomposition", "[", "A", "]"}]}], ";"}]], "Input", - CellLabel-> - "In[826]:=",ExpressionUUID->"246494f5-8cb8-4f04-aba9-2fdb96fa19b5"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"l", "=", - RowBox[{ - RowBox[{"lu", " ", - RowBox[{"SparseArray", "[", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"i_", ",", "j_"}], "}"}], "/;", - RowBox[{"j", "<", "i"}]}], "\[Rule]", "1"}], ",", - RowBox[{"{", - RowBox[{"5", ",", "5"}], "}"}]}], "]"}]}], "+", - RowBox[{"IdentityMatrix", "[", "5", "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8738201246059017`*^9, 3.8738201287340612`*^9}}, - CellLabel-> - "In[827]:=",ExpressionUUID->"aecfa433-ab5e-4bdf-95b5-ed828e6fe08e"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"u", "=", - RowBox[{"lu", " ", - RowBox[{"SparseArray", "[", - RowBox[{ - RowBox[{ - RowBox[{ - RowBox[{"{", - RowBox[{"i_", ",", "j_"}], "}"}], "/;", - RowBox[{"j", "\[GreaterEqual]", "i"}]}], "\[Rule]", "1"}], ",", - RowBox[{"{", - RowBox[{"5", ",", "5"}], "}"}]}], "]"}]}]}], ";"}]], "Input", - CellChangeTimes->{{3.8738201451434727`*^9, 3.873820146350107*^9}}, - CellLabel-> - "In[828]:=",ExpressionUUID->"789c9056-861a-4d2f-b50d-ccf966e52c55"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"b", "=", - RowBox[{"{", - RowBox[{ - RowBox[{"-", "10.19"}], ",", " ", "101.92", ",", " ", "43.16", ",", - RowBox[{"-", "123.63"}], ",", "4.24"}], "}"}]}], ";"}]], "Input", - CellChangeTimes->{{3.873820037876417*^9, 3.8738200541880493`*^9}, - 3.8738200977675657`*^9, {3.873820164312252*^9, 3.87382018528706*^9}}, - CellLabel-> - "In[829]:=",ExpressionUUID->"eb5fb3dc-d0e9-4c2a-9231-fbdee6a69981"], - -Cell[BoxData[ - RowBox[{ - RowBox[{"y", "=", - RowBox[{"LinearSolve", "[", - RowBox[{"l", ",", - RowBox[{"b", "[", - RowBox[{"[", "p", "]"}], "]"}]}], "]"}]}], ";"}]], "Input", - CellLabel-> - "In[830]:=",ExpressionUUID->"85ddb7f7-078d-4d06-9cfc-e57143fdf917"], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"LinearSolve", "[", - RowBox[{"u", ",", "y"}], "]"}]], "Input", - CellLabel-> - "In[831]:=",ExpressionUUID->"af25aa22-8833-4751-ae34-f83e5c012372"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"6.500000000000001`", ",", - RowBox[{"-", "5.3`"}], ",", "6.699999999999999`", ",", - RowBox[{"-", "4.9`"}], ",", "1.3999999999999995`"}], "}"}]], "Output", - CellChangeTimes->{ - 3.873820187689494*^9, 3.873820345957288*^9, 3.873820395653883*^9, - 3.873821529965413*^9, 3.873821587146599*^9, {3.8738216293517313`*^9, - 3.8738216567982807`*^9}, {3.873884591439056*^9, 3.87388461003594*^9}, - 3.873884878631164*^9, 3.8738849377106953`*^9, 3.873885017936191*^9, - 3.8738855045009193`*^9, 3.873885854479301*^9, 3.873885892376915*^9, - 3.8738860618686333`*^9, 3.873886216751912*^9, 3.873886304470621*^9, - 3.873886355593725*^9, 3.87388641494897*^9}, - CellLabel-> - "Out[831]=",ExpressionUUID->"92429466-a378-4b8f-969b-5d3ba34bc72f"] -}, Open ]], - -Cell[CellGroupData[{ - -Cell[BoxData[ - RowBox[{"LinearSolve", "[", - RowBox[{"A", ",", "b"}], "]"}]], "Input", - CellChangeTimes->{{3.873886404466*^9, 3.873886410436647*^9}}, - CellLabel-> - "In[832]:=",ExpressionUUID->"59ac2e84-59e5-4162-b4e4-d5c9e3ad879f"], - -Cell[BoxData[ - RowBox[{"{", - RowBox[{"6.499999999999998`", ",", - RowBox[{"-", "5.300000000000001`"}], ",", "6.700000000000001`", ",", - RowBox[{"-", "4.900000000000001`"}], ",", "1.3999999999999992`"}], - "}"}]], "Output", - CellChangeTimes->{3.873886414988861*^9}, - CellLabel-> - "Out[832]=",ExpressionUUID->"1ac6f99c-bb9d-4b9d-9b81-830934bfdaba"] -}, Open ]] -}, -WindowSize->{808, 911}, -WindowMargins->{{Automatic, 167}, {64, Automatic}}, -FrontEndVersion->"12.1 for Mac OS X x86 (64-bit) (March 18, 2020)", -StyleDefinitions->"Default.nb", -ExpressionUUID->"dcdbbb14-eb6d-4fed-ba85-937c1f60b95b" -] -(* End of Notebook Content *) - -(* Internal cache information *) -(*CellTagsOutline -CellTagsIndex->{} -*) -(*CellTagsIndex -CellTagsIndex->{} -*) -(*NotebookFileOutline -Notebook[{ -Cell[558, 20, 302, 7, 30, "Input",ExpressionUUID->"41e5b97b-b44f-4d15-87e6-9e053be85385"], -Cell[863, 29, 597, 16, 30, "Input",ExpressionUUID->"6f7b8d72-9554-4028-aa5b-4e447c8682f1"], -Cell[CellGroupData[{ -Cell[1485, 49, 257, 5, 30, "Input",ExpressionUUID->"f25673d5-c4ab-4d90-aff9-c1075f28a925"], -Cell[1745, 56, 1198, 28, 76, "Output",ExpressionUUID->"fa8a9d60-b48d-43ea-9780-80209e904b9e"] -}, Open ]], -Cell[2958, 87, 369, 9, 30, "Input",ExpressionUUID->"2b6cf90a-ed7e-46ba-9cbd-97ff2db95ff9"], -Cell[3330, 98, 335, 8, 30, "Input",ExpressionUUID->"817188e7-589a-4e7e-94fe-fa8020639d81"], -Cell[3668, 108, 340, 7, 30, "Input",ExpressionUUID->"21cbb4e7-9a74-4bfb-b1d4-55ae75260b4d"], -Cell[CellGroupData[{ -Cell[4033, 119, 302, 7, 30, "Input",ExpressionUUID->"b2739b32-3614-4b53-89be-12abf10d241c"], -Cell[4338, 128, 739, 13, 34, "Output",ExpressionUUID->"b71f7dcd-4d67-47e6-b472-015d133fb489"] -}, Open ]], -Cell[5092, 144, 768, 18, 73, "Input",ExpressionUUID->"9dec25d4-6691-4f80-95b6-6d31800fcc15"], -Cell[5863, 164, 358, 8, 30, "Input",ExpressionUUID->"d8e98558-a838-471f-891b-f5fde99a5806"], -Cell[CellGroupData[{ -Cell[6246, 176, 636, 14, 73, "Input",ExpressionUUID->"9daa7836-803d-4fec-b35e-e414268c5517"], -Cell[6885, 192, 1426, 35, 92, "Output",ExpressionUUID->"87fe9717-8fd0-4878-8162-f397c94a16ec"], -Cell[8314, 229, 1154, 26, 51, "Output",ExpressionUUID->"309e5573-4259-4baa-9368-d59ec2c596b6"] -}, Open ]], -Cell[9483, 258, 401, 9, 30, "Input",ExpressionUUID->"4a2871bb-acdf-4f0e-b33b-fe9c6e0d6f2b"], -Cell[CellGroupData[{ -Cell[9909, 271, 436, 11, 30, "Input",ExpressionUUID->"4a9e265f-de0c-48aa-a0ef-6f9c598e5c51"], -Cell[10348, 284, 969, 24, 76, "Output",ExpressionUUID->"4db56fae-df15-40dc-82dc-cea0d9768988"] -}, Open ]], -Cell[11332, 311, 642, 18, 30, "Input",ExpressionUUID->"bdab867a-b5a2-4fe0-836c-9bd36622b9ec"], -Cell[11977, 331, 583, 16, 30, "Input",ExpressionUUID->"d9347773-9ae3-4df3-a691-e0a3bf335ca7"], -Cell[CellGroupData[{ -Cell[12585, 351, 413, 8, 73, "Input",ExpressionUUID->"67ab2db9-006a-4dbc-918c-f1595187f78b"], -Cell[13001, 361, 1089, 27, 76, "Output",ExpressionUUID->"e91bb598-3e5b-42c7-8e0e-6eb46d06d4c5"], -Cell[14093, 390, 1210, 30, 76, "Output",ExpressionUUID->"7797d898-12b5-4f05-bfd8-9eaa69004166"], -Cell[15306, 422, 1032, 26, 76, "Output",ExpressionUUID->"ac97130c-200c-423e-834b-be1bfb97047a"] -}, Open ]], -Cell[16353, 451, 256, 5, 30, "Input",ExpressionUUID->"bfdf5b61-e9bf-45ca-8335-c223dda8f4d6"], -Cell[CellGroupData[{ -Cell[16634, 460, 309, 7, 30, "Input",ExpressionUUID->"5f62f4af-3da9-4418-a89f-9e9ed2034ecb"], -Cell[16946, 469, 638, 12, 34, "Output",ExpressionUUID->"7a5d7922-5f93-4aa7-ada6-b022923f8461"] -}, Open ]], -Cell[17599, 484, 259, 5, 30, "Input",ExpressionUUID->"99477342-5c67-45ac-8133-c92237cd5832"], -Cell[17861, 491, 387, 10, 30, "Input",ExpressionUUID->"282f3c34-4b37-4864-bb1c-1e55d0557afe"], -Cell[CellGroupData[{ -Cell[18273, 505, 236, 5, 30, "Input",ExpressionUUID->"2e9bafd5-c3f0-4f20-ada5-c244e9da15df"], -Cell[18512, 512, 687, 13, 34, "Output",ExpressionUUID->"9a17b002-ad5e-4b6c-908a-ca6093153154"] -}, Open ]], -Cell[19214, 528, 347, 7, 30, "Input",ExpressionUUID->"ee22a100-a557-4a76-9309-a46c1731d8c7"], -Cell[CellGroupData[{ -Cell[19586, 539, 238, 5, 30, "Input",ExpressionUUID->"cbca244e-4be6-4346-a7ab-dc5203e7bdef"], -Cell[19827, 546, 355, 8, 34, "Output",ExpressionUUID->"32fb9c23-f868-4c65-80f6-6f046aff4e6e"] -}, Open ]], -Cell[20197, 557, 280, 6, 30, "Input",ExpressionUUID->"343c9c1b-6f39-477f-8d94-059828fedb35"], -Cell[20480, 565, 1036, 28, 52, "Input",ExpressionUUID->"13a7c346-7eb1-46b0-86dc-035a5dfe3833"], -Cell[CellGroupData[{ -Cell[21541, 597, 210, 4, 30, "Input",ExpressionUUID->"47a2829d-8e01-4bb0-893f-5b6e49dc6e33"], -Cell[21754, 603, 1404, 37, 110, "Output",ExpressionUUID->"8573a0d9-eb3d-4586-ba90-761fef5af103"] -}, Open ]], -Cell[23173, 643, 253, 7, 30, "Input",ExpressionUUID->"37953d2b-014a-427a-887b-588a6aa10f2e"], -Cell[CellGroupData[{ -Cell[23451, 654, 280, 6, 30, "Input",ExpressionUUID->"8d2e9e4e-96ba-42c1-9fc3-e845f655e08f"], -Cell[23734, 662, 687, 12, 34, "Output",ExpressionUUID->"53329e55-30da-499d-949c-c7cfccc3b0f9"] -}, Open ]], -Cell[24436, 677, 303, 8, 30, "Input",ExpressionUUID->"626d7270-4e75-4b49-a8a8-42895eda885f"], -Cell[CellGroupData[{ -Cell[24764, 689, 509, 12, 73, "Input",ExpressionUUID->"dea06982-b0de-45c7-8f63-cc86c1ca456d"], -Cell[25276, 703, 1822, 53, 142, "Output",ExpressionUUID->"660f4a7a-63f1-4d84-bc33-76ef15c71a78"], -Cell[27101, 758, 1676, 46, 90, "Output",ExpressionUUID->"eb717ef3-5ecf-4d7a-a71e-646410641d53"] -}, Open ]], -Cell[28792, 807, 347, 9, 30, "Input",ExpressionUUID->"4b28e487-55a0-44ce-8ff6-707135d41744"], -Cell[CellGroupData[{ -Cell[29164, 820, 297, 8, 30, "Input",ExpressionUUID->"69ef9827-97bf-4f09-b8d0-d064d9d7e538"], -Cell[29464, 830, 819, 22, 110, "Output",ExpressionUUID->"c59573a8-ab3e-48ce-a02d-ed13c441b61f"] -}, Open ]], -Cell[30298, 855, 256, 5, 30, "Input",ExpressionUUID->"79f92fc0-4767-4a7f-aa0c-6d097ea669c3"], -Cell[30557, 862, 1094, 31, 52, "Input",ExpressionUUID->"7490cda9-db9e-4685-9141-0ea5557776e5"], -Cell[CellGroupData[{ -Cell[31676, 897, 210, 4, 30, "Input",ExpressionUUID->"157fafe0-d722-4a26-b1f2-4dd55a612811"], -Cell[31889, 903, 1537, 42, 110, "Output",ExpressionUUID->"14cc1f82-a6e4-4307-ad3d-a53257cb251e"] -}, Open ]], -Cell[33441, 948, 253, 7, 30, "Input",ExpressionUUID->"246494f5-8cb8-4f04-aba9-2fdb96fa19b5"], -Cell[33697, 957, 595, 17, 30, "Input",ExpressionUUID->"aecfa433-ab5e-4bdf-95b5-ed828e6fe08e"], -Cell[34295, 976, 530, 15, 30, "Input",ExpressionUUID->"789c9056-861a-4d2f-b50d-ccf966e52c55"], -Cell[34828, 993, 443, 10, 30, "Input",ExpressionUUID->"eb5fb3dc-d0e9-4c2a-9231-fbdee6a69981"], -Cell[35274, 1005, 272, 8, 30, "Input",ExpressionUUID->"85ddb7f7-078d-4d06-9cfc-e57143fdf917"], -Cell[CellGroupData[{ -Cell[35571, 1017, 170, 4, 30, "Input",ExpressionUUID->"af25aa22-8833-4751-ae34-f83e5c012372"], -Cell[35744, 1023, 781, 14, 34, "Output",ExpressionUUID->"92429466-a378-4b8f-969b-5d3ba34bc72f"] -}, Open ]], -Cell[CellGroupData[{ -Cell[36562, 1042, 233, 5, 30, "Input",ExpressionUUID->"59ac2e84-59e5-4162-b4e4-d5c9e3ad879f"], -Cell[36798, 1049, 355, 8, 34, "Output",ExpressionUUID->"1ac6f99c-bb9d-4b9d-9b81-830934bfdaba"] -}, Open ]] -} -] -*) - -(* End of internal cache information *) - diff --git a/src/utils/unit_test/doc/nonlinearSolverReference.py b/src/utils/unit_test/doc/nonlinearSolverReference.py deleted file mode 100644 index 2aff1ec..0000000 --- a/src/utils/unit_test/doc/nonlinearSolverReference.py +++ /dev/null @@ -1,40 +0,0 @@ -from scipy.optimize import fsolve -import math - - -##---------------------------------------------------------------------------## -def quadratic2d(p): - x, y = p - return (y * y - x + 1, x * x - y - 1) - - -x, y = fsolve(quadratic2d, (1, 1), xtol=1.0e-13) - -print(x, y) -print(quadratic2d((x, y))) - - -##---------------------------------------------------------------------------## -def linear3d(p): - x, y, z = p - return (x - y + z - 1, 2 * x - z, x + 0.5 * y + 0.5 * z - 3) - - -x, y, z = fsolve(linear3d, (1, 1, 1), xtol=1.0e-13) - -print(x, y, z) -print(linear3d((x, y, z))) - - -##---------------------------------------------------------------------------## -def quadratic3d(p): - x, y, z = p - return (x * y * z + y - 1, y * y + x * x - 3, x * z + y - 5) - - -x, y, z = fsolve(quadratic3d, (1, 1, 1), xtol=1.0e-13) - -print(x, y, z) -print(quadratic3d((x, y, z))) - -##---------------------------------------------------------------------------## diff --git a/src/utils/unit_test/doc/smoothMath_reference.py b/src/utils/unit_test/doc/smoothMath_reference.py deleted file mode 100644 index 8ba3d1a..0000000 --- a/src/utils/unit_test/doc/smoothMath_reference.py +++ /dev/null @@ -1,196 +0,0 @@ -import math -import numpy as np - - -# Returns normalized vector -def normalize(v): - norm = np.linalg.norm(v) - if norm == 0: - return v - return v / norm - - -# The implementation of norm(x, tol) in SmoothMath -# for vectors -def smooth_norm(x, tol): - dotp = np.dot(x, x) - tol2 = tol * tol - - if (tol2 <= dotp): - return np.sqrt(dotp) - else: - return 0.5 * (dotp + tol2) / tol - - -def deriv_smooth_norm(x, tol, dir): - dotp = np.dot(x, x) - tol2 = tol * tol - - if (tol2 <= dotp): - return x[dir] / np.sqrt(dotp) - else: - return x[dir] / tol - - -# The implementation of norm(v, M, tol) in SmoothMath -# for vectors and a metric tensor -def smooth_metric_norm(x, M, tol): - dotp = x.T @ M @ x - tol2 = tol * tol - - if (tol2 <= dotp): - return np.sqrt(dotp) - else: - return 0.5 * (dotp + tol2) / tol - - -# See the following -# https://math.stackexchange.com/questions/189434/derivative-of-quadratic-form -def deriv_smooth_metric_norm(x, M, tol, dir): - dotp = x.T @ M @ x - tol2 = tol * tol - Dx = (M + M.T) @ x - - if (tol2 <= dotp): - return 0.5 * Dx[dir] / np.sqrt(dotp) - else: - return 0.5 * Dx[dir] / tol - - -# Test vectors -v2 = np.array([0.25, -0.166]) -v3 = np.array([0.175, 0.33, -0.92]) - -# Tolerance -tol = 1.0 - -# Print the norm of one of the vectors -print('smooth_norm v2: (0.0) ', smooth_norm(v2, 0.0)) -print('smooth_norm v2: (%d) ' % tol, smooth_norm(v2, tol)) - -print('deriv_smooth_norm v2: (0.0) ', deriv_smooth_norm(v2, 0.0, 0)) -print('deriv_smooth_norm v2: (0.0) ', deriv_smooth_norm(v2, 0.0, 1)) - -print('deriv_smooth_norm v2: (%d) ' % tol, deriv_smooth_norm(v2, tol, 0)) -print('deriv_smooth_norm v2: (%d) ' % tol, deriv_smooth_norm(v2, tol, 1)) - -print('smooth_norm v3: (0.0) ', smooth_norm(v3, 0.0)) -print('smooth_norm v3: (%d) ' % tol, smooth_norm(v3, tol)) - -print('deriv_smooth_norm v3: (0.0) ', deriv_smooth_norm(v3, 0.0, 0)) -print('deriv_smooth_norm v3: (0.0) ', deriv_smooth_norm(v3, 0.0, 1)) -print('deriv_smooth_norm v3: (0.0) ', deriv_smooth_norm(v3, 0.0, 2)) - -print('deriv_smooth_norm v3: (%d) ' % tol, deriv_smooth_norm(v3, tol, 0)) -print('deriv_smooth_norm v3: (%d) ' % tol, deriv_smooth_norm(v3, tol, 1)) -print('deriv_smooth_norm v3: (%d) ' % tol, deriv_smooth_norm(v3, tol, 2)) - -# Construct two orthonormal basis vectors -a = [1.0, 1.0] -b = [-1.0, 1.0] - -# Normalize -a = normalize(a) -b = normalize(b) - -# Build a matrix of eigenvectors -U = np.vstack([a, b]) - -# Element lengths -h1 = 0.5 -h2 = 1.0 - -# Diagonal matrix -S = np.diag([h1, h2]) - -# Construct a 2d metric tensor -M2 = U.T @ S @ U -print(M2) -# Print the norm of one of the vectors -print('smooth_metric_norm v2: (0.0) ', smooth_metric_norm(v2, M2, 0.0)) -print('smooth_metric_norm v2: (%d) ' % tol, smooth_metric_norm(v2, M2, tol)) - -print('deriv_smooth_metric_norm v2: (0.0) ', - deriv_smooth_metric_norm(v2, M2, 0.0, 0)) -print('deriv_smooth_metric_norm v2: (0.0) ', - deriv_smooth_metric_norm(v2, M2, 0.0, 1)) - -print('deriv_smooth_metric_norm v2: (%d) ' % tol, - deriv_smooth_metric_norm(v2, M2, tol, 0)) -print('deriv_smooth_metric_norm v2: (%d) ' % tol, - deriv_smooth_metric_norm(v2, M2, tol, 1)) - -# Now create a 3d orthonormal basis -a = [1.0, 1.0, 0] -b = [-1.0, 1.0, 0] -c = np.cross(a, b) -a = normalize(a) -b = normalize(b) -c = normalize(c) - -U = np.vstack([a, b, c]) - -# Element lengths -h1 = 0.5 -h2 = 0.25 -h3 = 1.0 - -S = np.diag([h1, h2, h3]) - -# Construct a 3d metric tensor -M3 = U.T @ S @ U -print(M3) - -print('smooth_metric_norm v3: (0.0) ', smooth_metric_norm(v3, M3, 0.0)) -print('smooth_metric_norm v3: (%d) ' % tol, smooth_metric_norm(v3, M3, tol)) - -print('deriv_smooth_metric_norm v3: (0.0) ', - deriv_smooth_metric_norm(v3, M3, 0.0, 0)) -print('deriv_smooth_metric_norm v3: (0.0) ', - deriv_smooth_metric_norm(v3, M3, 0.0, 1)) -print('deriv_smooth_netric_norm v3: (0.0) ', - deriv_smooth_metric_norm(v3, M3, 0.0, 2)) - -print('deriv_smooth_metric_norm v3: (%d) ' % tol, - deriv_smooth_metric_norm(v3, M3, tol, 0)) -print('deriv_smooth_metric_norm v3: (%d) ' % tol, - deriv_smooth_metric_norm(v3, M3, tol, 1)) -print('deriv_smooth_metric_norm v3: (%d) ' % tol, - deriv_smooth_metric_norm(v3, M3, tol, 2)) - -# Now test the identity -M2 = np.array([[1.0, 0.0], [0.0, 1.0]]) -print(M2) -print('smooth_metric_norm v2: (0.0) %.18f' % smooth_metric_norm(v2, M2, 0.0)) -print('smooth_metric_norm v2: (%d) %.18f' % - (tol, smooth_metric_norm(v2, M2, tol))) - -print('deriv_smooth_metric_norm v2: (0.0) %.18f' % - deriv_smooth_metric_norm(v2, M2, 0.0, 0)) -print('deriv_smooth_metric_norm v2: (0.0) %.18f' % - deriv_smooth_metric_norm(v2, M2, 0.0, 1)) - -print('deriv_smooth_metric_norm v2: (%d) %.18f' % - (tol, deriv_smooth_metric_norm(v2, M2, tol, 0))) -print('deriv_smooth_metric_norm v2: (%d) %.18f' % - (tol, deriv_smooth_metric_norm(v2, M2, tol, 1))) - -M3 = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) -print(M3) -print('smooth_metric_norm v3: (0.0) %.18f' % smooth_metric_norm(v3, M3, 0.0)) -print('smooth_metric_norm v3: (%d) %.18f' % - (tol, smooth_metric_norm(v3, M3, tol))) - -print('deriv_smooth_metric_norm v3: (0.0) %.18f' % - deriv_smooth_metric_norm(v3, M3, 0.0, 0)) -print('deriv_smooth_netric_norm v3: (0.0) %.18f' % - deriv_smooth_metric_norm(v3, M3, 0.0, 1)) -print('deriv_smooth_netric_norm v3: (0.0) %.18f' % - deriv_smooth_metric_norm(v3, M3, 0.0, 2)) - -print('deriv_smooth_metric_norm v3: (%d) %.18f' % - (tol, deriv_smooth_metric_norm(v3, M3, tol, 0))) -print('deriv_smooth_metric_norm v3: (%d) %.18f' % - (tol, deriv_smooth_metric_norm(v3, M3, tol, 1))) -print('deriv_smooth_metric_norm v3: (%d) %.18f' % - (tol, deriv_smooth_metric_norm(v3, M3, tol, 2))) diff --git a/src/utils/unit_test/tstConstants.cpp b/src/utils/unit_test/tstConstants.cpp deleted file mode 100644 index 97ce8bd..0000000 --- a/src/utils/unit_test/tstConstants.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include - -#include - -#include - -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -void constantTest() -{ - // Test on the CPU. - EXPECT_EQ(std::acos(-1.0), Constants::pi); - EXPECT_EQ(acosf(-1.0f), Constants::pi_v); - EXPECT_EQ(acosl(-1.0L), Constants::pi_v); - - // Results views for testing on device. - // long double is not available in CUDA. - Kokkos::View dbl_result("dbl_result"); - Kokkos::View flt_result("flt_result"); - - // Assign constant in a kernel. - Kokkos::parallel_for( - "pi", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = Constants::pi; - flt_result(0) = Constants::pi_v; - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto flt_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, flt_result); - - // Make sure device value matches host value. - EXPECT_EQ(Constants::pi, dbl_host(0)); - EXPECT_EQ(Constants::pi_v, flt_host(0)); -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(Constants, pi) -{ - constantTest(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test diff --git a/src/utils/unit_test/tstMatrixMath.cpp b/src/utils/unit_test/tstMatrixMath.cpp deleted file mode 100644 index 819468a..0000000 --- a/src/utils/unit_test/tstMatrixMath.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include - -#include - -#include - -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -void lupDecompAndSolveTest3x3() -{ - constexpr int dim = 3; - Kokkos::View A("A"); - Kokkos::View p("p"); - Kokkos::View b("b"); - Kokkos::View w("w"); - - // Create host mirror views - auto A_host = Kokkos::create_mirror_view(A); - auto p_host = Kokkos::create_mirror_view(p); - auto b_host = Kokkos::create_mirror_view(b); - - A_host(0, 0) = 1; - A_host(0, 1) = 2; - A_host(0, 2) = 4; - - A_host(1, 0) = 2; - A_host(1, 1) = 1; - A_host(1, 2) = 3; - - A_host(2, 0) = 3; - A_host(2, 1) = 2; - A_host(2, 2) = 4; - - b_host(0) = 3; - b_host(1) = 4; - b_host(2) = 5; - - // Deep copy the initialized view to device - Kokkos::deep_copy(A, A_host); - Kokkos::deep_copy(b, b_host); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "lu_decomp_and_solve", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - MatrixMath::LUP(A, p); - MatrixMath::LUP_solve(A, p, w, b); - }); - - // Deep copy results to host - Kokkos::deep_copy(A_host, A); - Kokkos::deep_copy(p_host, p); - Kokkos::deep_copy(b_host, b); - - const double A_exp[dim][dim] - = {{1. / 3., 4. / 3., 8. / 3.}, {2. / 3., -1. / 4., 1.}, {3., 2., 4.}}; - - for (int i = 0; i < dim; ++i) - for (int j = 0; j < dim; ++j) - EXPECT_DOUBLE_EQ(A_exp[i][j], A_host(i, j)); - - const int p_exp[dim] = {2, 0, 1}; - const double b_exp[dim] = {1, -1, 1}; - - for (int i = 0; i < dim; ++i) - { - EXPECT_EQ(p_exp[i], p_host(i)); - EXPECT_DOUBLE_EQ(b_exp[i], b_host(i)); - } -} - -//---------------------------------------------------------------------------// -void lupDecompTest5x5() -{ - constexpr int dim = 5; - Kokkos::View A("A"); - Kokkos::View p("p"); - - // Create host mirror views - auto A_host = Kokkos::create_mirror_view(A); - auto p_host = Kokkos::create_mirror_view(p); - - A_host(0, 0) = 8; - A_host(0, 1) = -2; - A_host(0, 2) = -8; - A_host(0, 3) = 9; - A_host(0, 4) = 6; - - A_host(1, 0) = 9; - A_host(1, 1) = 5; - A_host(1, 2) = 5; - A_host(1, 3) = 3; - A_host(1, 4) = 1; - - A_host(2, 0) = 9; - A_host(2, 1) = -7; - A_host(2, 2) = 2; - A_host(2, 3) = 8; - A_host(2, 4) = 4; - - A_host(3, 0) = 9; - A_host(3, 1) = 6; - A_host(3, 2) = -9; - A_host(3, 3) = 0; - A_host(3, 4) = -8; - - A_host(4, 0) = -7; - A_host(4, 1) = 9; - A_host(4, 2) = -9; - A_host(4, 3) = -7; - A_host(4, 4) = 6; - - // Deep copy the initialized view to device - Kokkos::deep_copy(A, A_host); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "lu_decomp", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { MatrixMath::LUP(A, p); }); - - // Deep copy results to host - Kokkos::deep_copy(A_host, A); - Kokkos::deep_copy(p_host, p); - - const double A_exp[dim][dim] - = {{8.0 / 9.0, -0.5, -15.0, 4.0, 8.5}, - {9.0, 5.0, 5.0, 3.0, 1.0}, - {1.0, -27.0 / 29.0, 15.0 / 29.0, 410.0 / 1817.0, 31989.0 / 3634.0}, - {1.0, 9.0 / 116.0, 263.0 / 290.0, -1817.0 / 290.0, -2499.0 / 145.0}, - {-7.0 / 9.0, 116.0 / 9.0, -46.0 / 9.0, -14.0 / 3.0, 61.0 / 9.0}}; - - for (int i = 0; i < dim; ++i) - for (int j = 0; j < dim; ++j) - EXPECT_NEAR(A_exp[i][j], A_host(i, j), 5.0e-15); - - const int p_exp[dim] = {1, 4, 0, 3, 2}; - - for (int i = 0; i < dim; ++i) - EXPECT_EQ(p_exp[i], p_host(i)); -} - -//---------------------------------------------------------------------------// -void lupSolveTest5x5() -{ - constexpr int dim = 5; - Kokkos::View A("A"); - Kokkos::View p("p"); - Kokkos::View b("b"); - Kokkos::View w("w"); - - // Create host mirror views - auto A_host = Kokkos::create_mirror_view(A); - auto b_host = Kokkos::create_mirror_view(b); - - A_host(0, 0) = -2.6; - A_host(0, 1) = -7.2; - A_host(0, 2) = -0.4; - A_host(0, 3) = 7.5; - A_host(0, 4) = 5.7; - - A_host(1, 0) = 2.2; - A_host(1, 1) = -4.9; - A_host(1, 2) = 3.8; - A_host(1, 3) = -8.7; - A_host(1, 4) = -4.6; - - A_host(2, 0) = 4.8; - A_host(2, 1) = -4.6; - A_host(2, 2) = 5.7; - A_host(2, 3) = 7.9; - A_host(2, 4) = -8.5; - - A_host(3, 0) = -4.5; - A_host(3, 1) = 1.4; - A_host(3, 2) = -9.5; - A_host(3, 3) = 2.7; - A_host(3, 4) = -7.2; - - A_host(4, 0) = 2.1; - A_host(4, 1) = -2.6; - A_host(4, 2) = -7.4; - A_host(4, 3) = -5.3; - A_host(4, 4) = 0.3; - - b_host(0) = -10.19; - b_host(1) = 101.92; - b_host(2) = 43.16; - b_host(3) = -123.63; - b_host(4) = 4.24; - - // Deep copy the initialized view to device - Kokkos::deep_copy(A, A_host); - Kokkos::deep_copy(b, b_host); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "lu_decomp", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - MatrixMath::LUP(A, p); - MatrixMath::LUP_solve(A, p, w, b); - }); - - // Deep copy results to host - Kokkos::deep_copy(A_host, A); - Kokkos::deep_copy(b_host, b); - - const double b_exp[dim] = {6.5, -5.3, 6.7, -4.9, 1.4}; - - for (int i = 0; i < dim; ++i) - EXPECT_DOUBLE_EQ(b_exp[i], b_host(i)); -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(MatrixMath, lupDecomp3x3) -{ - lupDecompAndSolveTest3x3(); -} -//---------------------------------------------------------------------------// -TEST(MatrixMath, lupDecomp5x5) -{ - lupDecompTest5x5(); -} -//---------------------------------------------------------------------------// -TEST(MatrixMath, lupSolve5x5) -{ - lupSolveTest5x5(); -} -//---------------------------------------------------------------------------// -} // namespace Test diff --git a/src/utils/unit_test/tstNonlinearSolver.cpp b/src/utils/unit_test/tstNonlinearSolver.cpp deleted file mode 100644 index b5214e2..0000000 --- a/src/utils/unit_test/tstNonlinearSolver.cpp +++ /dev/null @@ -1,450 +0,0 @@ -#include - -#include - -#include - -#include - -#include - -#include - -#include -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -// solver parameters -constexpr double nonlinear_tol = 1.0e-12; -constexpr int nonlinear_max_iter = 10; - -//---------------------------------------------------------------------------// -// create views -auto createView(panzer::Traits::Residual, const int size) -{ - using scalar_type = typename panzer::Traits::Residual::ScalarT; - return Kokkos::View("x", size); -} - -auto createView(panzer::Traits::Jacobian, const int size) -{ - using scalar_type = typename panzer::Traits::Jacobian::ScalarT; - return Kokkos::View("x", size, 1); -} - -//---------------------------------------------------------------------------// -// Solve on device. -template -bool solve(std::integral_constant, - Kokkos::View& x, - const ResidualFunc& F, - const double tolerance, - const int max_iters) -{ - // Make device data. - auto x_dev = Kokkos::create_mirror_view(PHX::mem_space{}, x); - Kokkos::deep_copy(x_dev, x); - Kokkos::View result("solve_result"); - - // Solve. - Kokkos::parallel_for( - "nonlinear_solve", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - Kokkos::Array u; - for (std::size_t i = 0; i < N; ++i) - u[i] = x_dev(i); - result(0) - = Utils::NonlinearSolver::solve(u, F, tolerance, max_iters); - for (std::size_t i = 0; i < N; ++i) - x_dev(i) = u[i]; - }); - - // Move data to host. - Kokkos::deep_copy(x, x_dev); - auto result_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, result); - return result_host(0); -} - -//---------------------------------------------------------------------------// -// Linear Function 1D -//---------------------------------------------------------------------------// -struct LinearFunc1d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = 2.0 * u[0] + 1.0; - } -}; - -//---------------------------------------------------------------------------// -template -void runLinearTest1d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 1); - x(0) = 4.5; - auto result = solve(std::integral_constant{}, - x, - LinearFunc1d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(0)), -0.5, nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, linear_1_residual) -{ - runLinearTest1d(); -} - -TEST(NonlinearSolver, linear_1_jacobian) -{ - runLinearTest1d(); -} - -//---------------------------------------------------------------------------// -// Quadratic Function 1D -//---------------------------------------------------------------------------// -struct QuadraticFunc1d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = 2.0 * u[0] * u[0] - u[0] - 3.0; - } -}; - -//---------------------------------------------------------------------------// -template -void runQuadraticTest1d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 1); - - // Root 1 - x(0) = 5.0; - auto result = solve(std::integral_constant{}, - x, - QuadraticFunc1d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(0)), 1.5, nonlinear_tol); - - // Root 2 - x(0) = -2.0; - result = solve(std::integral_constant{}, - x, - QuadraticFunc1d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(0)), -1.0, nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, quadratic_1_residual) -{ - runQuadraticTest1d(); -} - -TEST(NonlinearSolver, quadratic_1_jacobian) -{ - runQuadraticTest1d(); -} - -//---------------------------------------------------------------------------// -// Linear Function 2D -//---------------------------------------------------------------------------// -struct LinearFunc2d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = 2.0 * u[0] + 3.0 * u[1] - 1.0; - f[1] = u[0] - 0.5 * u[1]; - } -}; - -//---------------------------------------------------------------------------// -template -void runLinearTest2d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 2); - x(0) = 1.5; - x(1) = -1.0; - auto result = solve(std::integral_constant{}, - x, - LinearFunc2d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(0)), 0.125, nonlinear_tol); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(1)), 0.25, nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, linear_2_residual) -{ - runLinearTest2d(); -} - -TEST(NonlinearSolver, linear_2_jacobian) -{ - runLinearTest2d(); -} - -//---------------------------------------------------------------------------// -// Quadratic Function 2D -//---------------------------------------------------------------------------// -struct QuadraticFunc2d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = u[1] * u[1] - u[0] + 1.0; - f[1] = u[0] * u[0] - u[1] - 1.0; - } -}; - -//---------------------------------------------------------------------------// -template -void runQuadraticTest2d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 2); - - // Root 1 - x(0) = 1.0; - x(1) = 1.0; - auto result = solve(std::integral_constant{}, - x, - QuadraticFunc2d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR(Sacado::ScalarValue::eval(x(0)), - 1.2055694304005904, - nonlinear_tol); - EXPECT_NEAR(Sacado::ScalarValue::eval(x(1)), - 0.45339765151640377, - nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, quadratic_2_residual) -{ - runQuadraticTest2d(); -} - -TEST(NonlinearSolver, quadratic_2_jacobian) -{ - runQuadraticTest2d(); -} - -//---------------------------------------------------------------------------// -// Linear Function 3D -//---------------------------------------------------------------------------// -struct LinearFunc3d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = u[0] - u[1] + u[2] - 1.0; - f[1] = 2.0 * u[0] - u[2]; - f[2] = u[0] + 0.5 * u[1] + 0.5 * u[2] - 3.0; - } -}; - -//---------------------------------------------------------------------------// -template -void runLinearTest3d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 3); - x(0) = 1.0; - x(1) = 1.0; - x(2) = 1.0; - auto result = solve(std::integral_constant{}, - x, - LinearFunc3d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(0)), 1.0, nonlinear_tol); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(1)), 2.0, nonlinear_tol); - EXPECT_NEAR( - Sacado::ScalarValue::eval(x(2)), 2.0, nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, linear_3_residual) -{ - runLinearTest3d(); -} - -TEST(NonlinearSolver, linear_3_jacobian) -{ - runLinearTest3d(); -} - -//---------------------------------------------------------------------------// -// Quadratic Function 3D -//---------------------------------------------------------------------------// -struct QuadraticFunc3d -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array& u, - Kokkos::Array& f) const - { - f[0] = u[0] * u[1] * u[2] + u[1] - 1.0; - f[1] = u[1] * u[1] + u[0] * u[0] - 3.0; - f[2] = u[0] * u[2] + u[1] - 5.0; - } -}; - -//---------------------------------------------------------------------------// -template -void runQuadraticTest3d() -{ - using scalar_type = typename EvalType::ScalarT; - auto x = createView(EvalType{}, 3); - - // Root 1 - x(0) = 1.0; - x(1) = 1.0; - x(2) = 1.0; - auto result = solve(std::integral_constant{}, - x, - QuadraticFunc3d{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_TRUE(result); - EXPECT_NEAR(Sacado::ScalarValue::eval(x(0)), - 1.7235320561211331, - nonlinear_tol); - EXPECT_NEAR(Sacado::ScalarValue::eval(x(1)), - 0.1715728752538099, - nonlinear_tol); - EXPECT_NEAR(Sacado::ScalarValue::eval(x(2)), - 2.8014721905507973, - nonlinear_tol); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, quadratic_3_residual) -{ - runQuadraticTest3d(); -} - -TEST(NonlinearSolver, quadratic_3_jacobian) -{ - runQuadraticTest3d(); -} - -//---------------------------------------------------------------------------// -// Maximum iteration failure -//---------------------------------------------------------------------------// -template -void failMaxIterTest() -{ - auto x = createView(EvalType{}, Dim); - - // Root 1 - for (int d = 0; d < Dim; ++d) - { - x(d) = 1.0; - } - auto result = solve( - std::integral_constant{}, x, Func{}, nonlinear_tol, 1); - EXPECT_FALSE(result); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, fail_max_iter_residual) -{ - failMaxIterTest(); - failMaxIterTest(); - failMaxIterTest(); -} - -TEST(NonlinearSolver, fail_max_iter_jacobian) -{ - failMaxIterTest(); - failMaxIterTest(); - failMaxIterTest(); -} - -//---------------------------------------------------------------------------// -// Degenerate Jacobian failure -//---------------------------------------------------------------------------// -template -struct DegenerateFunc -{ - template - KOKKOS_INLINE_FUNCTION void operator()(const Kokkos::Array&, - Kokkos::Array& f) const - { - for (int d = 0; d < Dim; ++d) - { - f[d] = 1.0; - } - } -}; - -//---------------------------------------------------------------------------// -template -void runDegenerateTest() -{ - auto x = createView(EvalType{}, Dim); - for (int d = 0; d < Dim; ++d) - { - x(d) = 4.5; - } - auto result = solve(std::integral_constant{}, - x, - DegenerateFunc{}, - nonlinear_tol, - nonlinear_max_iter); - EXPECT_FALSE(result); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) - -//---------------------------------------------------------------------------// -TEST(NonlinearSolver, degenerate_jacobian_residual) -{ - runDegenerateTest(); - runDegenerateTest(); - runDegenerateTest(); -} - -TEST(NonlinearSolver, degenerate_jacobian_jacobian) -{ - runDegenerateTest(); - runDegenerateTest(); - runDegenerateTest(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test diff --git a/src/utils/unit_test/tstParameterPack.cpp b/src/utils/unit_test/tstParameterPack.cpp deleted file mode 100644 index 30bc8a1..0000000 --- a/src/utils/unit_test/tstParameterPack.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include - -#include - -#include - -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -void captureTest() -{ - // Make some Kokkos views. - Kokkos::View dbl_view("dbl_view"); - Kokkos::View int_view("int_view"); - - // Make a parameter pack so we can capture them as a group. - auto pack = Utils::makeParameterPack(dbl_view, int_view); - - // Update the pack in a kernel - Kokkos::parallel_for( - "fill_pack", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - auto dv = get<0>(pack); - auto iv = get<1>(pack); - - dv(0) = 3.14; - iv(0, 0) = 12; - }); - - // Check the capture. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), dbl_view); - auto int_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), int_view); - - EXPECT_EQ(3.14, dbl_host(0)); - EXPECT_EQ(12, int_host(0, 0)); -} - -//---------------------------------------------------------------------------// -void emptyTest() -{ - std::ignore = Utils::makeParameterPack(); -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(TEST_CATEGORY, parameter_pack_capture) -{ - captureTest(); -} - -TEST(TEST_CATEGORY, parameter_pack_empty) -{ - emptyTest(); -} - -//---------------------------------------------------------------------------// - -} // end namespace Test diff --git a/src/utils/unit_test/tstScalarToVector.cpp b/src/utils/unit_test/tstScalarToVector.cpp deleted file mode 100644 index 664e4a5..0000000 --- a/src/utils/unit_test/tstScalarToVector.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace VertexCFD -{ -namespace Test -{ -//---------------------------------------------------------------------------// -template -struct Dependencies : public panzer::EvaluatorWithBaseImpl, - public PHX::EvaluatorDerived -{ - using scalar_type = typename EvalType::ScalarT; - int num_vel_dim; - - // Array of field solutions - std::vector> velocities; - std::vector> - dxdt_velocities; - std::vector> - grad_velocities; - - Dependencies(const panzer::IntegrationRule& ir, int dim) - : num_vel_dim(dim) - { - velocities.resize(num_vel_dim); - dxdt_velocities.resize(num_vel_dim); - grad_velocities.resize(num_vel_dim); - std::string name; - for (int dim = 0; dim < num_vel_dim; ++dim) - { - name = "velocity_" + std::to_string(dim); - velocities[dim] - = PHX::MDField( - name, ir.dl_scalar); - - name = "DXDT_velocity_" + std::to_string(dim); - dxdt_velocities[dim] - = PHX::MDField( - name, ir.dl_scalar); - - name = "GRAD_velocity_" + std::to_string(dim); - grad_velocities[dim] = PHX:: - MDField( - name, ir.dl_vector); - - this->addEvaluatedField(velocities[dim]); - this->addEvaluatedField(dxdt_velocities[dim]); - this->addEvaluatedField(grad_velocities[dim]); - } - - this->setName("ScalarToVector Unit Test Dependencies"); - } - - void evaluateFields(typename panzer::Traits::EvalData workset) override - { - const int num_vel_dim = velocities.size(); - const int num_points = velocities[0].extent(1); - - for (int vel_dim = 0; vel_dim < num_vel_dim; ++vel_dim) - { - // Create host mirrors of fields - auto vel_view = velocities[vel_dim].get_view(); - auto dxdt_vel_view = dxdt_velocities[vel_dim].get_view(); - auto grad_vel_view = grad_velocities[vel_dim].get_view(); - auto host_vel = Kokkos::create_mirror(vel_view); - auto host_dxdt_vel = Kokkos::create_mirror(dxdt_vel_view); - auto host_grad_vel = Kokkos::create_mirror(grad_vel_view); - - for (int cell = 0; cell < workset.num_cells; ++cell) - { - for (int point = 0; point < num_points; ++point) - { - // Set velocity values - double val = static_cast(vel_dim + 3 * point); - host_vel(cell, point) = val; - - // Set dxdt_velocity values - val = static_cast(100 + vel_dim + 3 * point); - host_dxdt_vel(cell, point) = val; - - // Set grad_velocity values - int num_space_dim = grad_vel_view.extent(2); - for (int space_dim = 0; space_dim < num_space_dim; - ++space_dim) - { - val = static_cast(vel_dim + 3 * point - + 9 * space_dim); - host_grad_vel(cell, point, space_dim) = val; - } - } - } - Kokkos::deep_copy(vel_view, host_vel); - Kokkos::deep_copy(dxdt_vel_view, host_dxdt_vel); - Kokkos::deep_copy(grad_vel_view, host_grad_vel); - } - } -}; - -template -void testEval() -{ - // Setup test fixture. - constexpr int num_vel_dim = NumVelDim; - const int integration_order = 2; - const int basis_order = 1; - EvaluatorTestFixture test_fixture( - num_vel_dim, integration_order, basis_order); - - auto& ir = *test_fixture.ir; - - // Create test dependency to set initial fields - auto deps = Teuchos::rcp(new Dependencies(ir, num_vel_dim)); - test_fixture.registerEvaluator(deps); - - // Create evaluator. - auto eval = Teuchos::rcp(new Utils::ScalarToVector( - ir, "velocity", num_vel_dim, true)); - test_fixture.registerEvaluator(eval); - - // Add required test fields. - test_fixture.registerTestField(eval->_vector_fields); - test_fixture.registerTestField(eval->_vector_dxdt_fields); - test_fixture.registerTestField(eval->_vector_grad_fields); - - // Evaluate - test_fixture.evaluate(); - - // Check the values - const auto vector_vel - = test_fixture.getTestFieldData(eval->_vector_fields); - const auto vector_dxdt_vel - = test_fixture.getTestFieldData(eval->_vector_dxdt_fields); - const auto vector_grad_vel - = test_fixture.getTestFieldData(eval->_vector_grad_fields); - - // Check the solution - const int num_point = ir.num_points; - for (int qp = 0; qp < num_point; ++qp) - { - for (int vel_dim = 0; vel_dim < num_vel_dim; ++vel_dim) - { - // Test velocity - double expected = static_cast(vel_dim + 3 * qp); - EXPECT_DOUBLE_EQ(expected, fieldValue(vector_vel, 0, qp, vel_dim)); - - // Test dxdt_velocity - expected = static_cast(100 + vel_dim + 3 * qp); - EXPECT_DOUBLE_EQ(expected, - fieldValue(vector_dxdt_vel, 0, qp, vel_dim)); - - // Test grad_velocity - for (int space_dim = 0; space_dim < NumSpaceDim; ++space_dim) - { - expected - = static_cast(vel_dim + 3 * qp + 9 * space_dim); - EXPECT_DOUBLE_EQ( - expected, - fieldValue(vector_grad_vel, 0, qp, space_dim, vel_dim)); - } - } - } -} - -//---------------------------------------------------------------------------// -TEST(S2V_22, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(S2V_22, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(S2V_33, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(S2V_33, jacobian_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(S2V_32, residual_test) -{ - testEval(); -} - -//---------------------------------------------------------------------------// -TEST(S2V_32, jacobian_test) -{ - testEval(); -} - -} // end namespace Test -} // end namespace VertexCFD diff --git a/src/utils/unit_test/tstSmoothMath.cpp b/src/utils/unit_test/tstSmoothMath.cpp deleted file mode 100644 index 210f04e..0000000 --- a/src/utils/unit_test/tstSmoothMath.cpp +++ /dev/null @@ -1,1162 +0,0 @@ -#include - -#include - -#include - -#include - -#include - -#include -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -void absTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 5; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 1.0e-2; - Kokkos::Array x_dbl = {3.4, 0.009, 0.0, -0.007, -1.2}; - Kokkos::Array x_fad; - for (int i = 0; i < num_result; ++i) - { - x_fad[i] = x_dbl[i]; - x_fad[i].diff(0, 1); - } - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_abs", - Kokkos::RangePolicy(0, num_result), - KOKKOS_LAMBDA(const int i) { - dbl_result(i) = SmoothMath::abs(x_dbl[i], tol); - fad_result(i) = SmoothMath::abs(x_fad[i], tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - // Check the value results. - EXPECT_DOUBLE_EQ(3.4, dbl_host(0)); - EXPECT_DOUBLE_EQ(0.00905, dbl_host(1)); - EXPECT_DOUBLE_EQ(0.005, dbl_host(2)); - EXPECT_DOUBLE_EQ(0.00745, dbl_host(3)); - EXPECT_DOUBLE_EQ(1.2, dbl_host(4)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(3.4, fad_host(0).val()); - EXPECT_DOUBLE_EQ(0.00905, fad_host(1).val()); - EXPECT_DOUBLE_EQ(0.005, fad_host(2).val()); - EXPECT_DOUBLE_EQ(0.00745, fad_host(3).val()); - EXPECT_DOUBLE_EQ(1.2, fad_host(4).val()); - - EXPECT_DOUBLE_EQ(1.0, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.9, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.7, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-1.0, fad_host(4).fastAccessDx(0)); -} - -//---------------------------------------------------------------------------// -void minTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 8; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 1.0e-2; - Kokkos::Array x_dbl = {3.4, 3.401}; - Kokkos::Array x_fad; - x_fad[0] = x_dbl[0]; - x_fad[0].diff(0, 2); - x_fad[1] = x_dbl[1]; - x_fad[1].diff(1, 2); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_min", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::min(x_dbl[0], x_dbl[1], 0.0); - fad_result(0) = SmoothMath::min(x_fad[0], x_fad[1], 0.0); - - dbl_result(1) = SmoothMath::min(x_dbl[0], x_dbl[1], tol); - fad_result(1) = SmoothMath::min(x_fad[0], x_fad[1], tol); - - dbl_result(2) = SmoothMath::min(x_dbl[1], x_dbl[0], 0.0); - fad_result(2) = SmoothMath::min(x_fad[1], x_fad[0], 0.0); - - dbl_result(3) = SmoothMath::min(x_dbl[1], x_dbl[0], tol); - fad_result(3) = SmoothMath::min(x_fad[1], x_fad[0], tol); - - dbl_result(4) = SmoothMath::min(x_dbl[0], x_dbl[0], 0.0); - fad_result(4) = SmoothMath::min(x_fad[0], x_fad[0], 0.0); - - dbl_result(5) = SmoothMath::min(x_dbl[0], x_dbl[0], tol); - fad_result(5) = SmoothMath::min(x_fad[0], x_fad[0], tol); - - dbl_result(6) = SmoothMath::min(x_dbl[1], x_dbl[1], 0.0); - fad_result(6) = SmoothMath::min(x_fad[1], x_fad[1], 0.0); - - dbl_result(7) = SmoothMath::min(x_dbl[1], x_dbl[1], tol); - fad_result(7) = SmoothMath::min(x_fad[1], x_fad[1], tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - // Check the value results. - EXPECT_DOUBLE_EQ(3.4, dbl_host(0)); - EXPECT_DOUBLE_EQ(3.397975, dbl_host(1)); - EXPECT_DOUBLE_EQ(3.4, dbl_host(2)); - EXPECT_DOUBLE_EQ(3.397975, dbl_host(3)); - EXPECT_DOUBLE_EQ(3.4, dbl_host(4)); - EXPECT_DOUBLE_EQ(3.3975, dbl_host(5)); - EXPECT_DOUBLE_EQ(3.401, dbl_host(6)); - EXPECT_DOUBLE_EQ(3.3985, dbl_host(7)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(3.4, fad_host(0).val()); - EXPECT_DOUBLE_EQ(3.397975, fad_host(1).val()); - EXPECT_DOUBLE_EQ(3.4, fad_host(2).val()); - EXPECT_DOUBLE_EQ(3.397975, fad_host(3).val()); - EXPECT_DOUBLE_EQ(3.4, fad_host(4).val()); - EXPECT_DOUBLE_EQ(3.3975, fad_host(5).val()); - EXPECT_DOUBLE_EQ(3.401, fad_host(6).val()); - EXPECT_DOUBLE_EQ(3.3985, fad_host(7).val()); - - EXPECT_DOUBLE_EQ(1.0, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.5499999999999945, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.5499999999999945, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(4).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(5).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(6).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(7).fastAccessDx(0)); - - EXPECT_DOUBLE_EQ(0.0, fad_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.4500000000000055, fad_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(2).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.4500000000000055, fad_host(3).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(4).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(5).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(1.0, fad_host(6).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(1.0, fad_host(7).fastAccessDx(1)); -} - -//---------------------------------------------------------------------------// -void maxTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 8; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 1.0e-2; - Kokkos::Array x_dbl = {3.4, 3.401}; - Kokkos::Array x_fad; - x_fad[0] = x_dbl[0]; - x_fad[0].diff(0, 2); - x_fad[1] = x_dbl[1]; - x_fad[1].diff(1, 2); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_max", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::max(x_dbl[0], x_dbl[1], 0.0); - fad_result(0) = SmoothMath::max(x_fad[0], x_fad[1], 0.0); - - dbl_result(1) = SmoothMath::max(x_dbl[0], x_dbl[1], tol); - fad_result(1) = SmoothMath::max(x_fad[0], x_fad[1], tol); - - dbl_result(2) = SmoothMath::max(x_dbl[1], x_dbl[0], 0.0); - fad_result(2) = SmoothMath::max(x_fad[1], x_fad[0], 0.0); - - dbl_result(3) = SmoothMath::max(x_dbl[1], x_dbl[0], tol); - fad_result(3) = SmoothMath::max(x_fad[1], x_fad[0], tol); - - dbl_result(4) = SmoothMath::max(x_dbl[0], x_dbl[0], 0.0); - fad_result(4) = SmoothMath::max(x_fad[0], x_fad[0], 0.0); - - dbl_result(5) = SmoothMath::max(x_dbl[0], x_dbl[0], tol); - fad_result(5) = SmoothMath::max(x_fad[0], x_fad[0], tol); - - dbl_result(6) = SmoothMath::max(x_dbl[1], x_dbl[1], 0.0); - fad_result(6) = SmoothMath::max(x_fad[1], x_fad[1], 0.0); - - dbl_result(7) = SmoothMath::max(x_dbl[1], x_dbl[1], tol); - fad_result(7) = SmoothMath::max(x_fad[1], x_fad[1], tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - // Check the value results. - EXPECT_DOUBLE_EQ(3.401, dbl_host(0)); - EXPECT_DOUBLE_EQ(3.403025, dbl_host(1)); - EXPECT_DOUBLE_EQ(3.401, dbl_host(2)); - EXPECT_DOUBLE_EQ(3.403025, dbl_host(3)); - EXPECT_DOUBLE_EQ(3.4, dbl_host(4)); - EXPECT_DOUBLE_EQ(3.4025, dbl_host(5)); - EXPECT_DOUBLE_EQ(3.401, dbl_host(6)); - EXPECT_DOUBLE_EQ(3.4035, dbl_host(7)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(3.401, fad_host(0).val()); - EXPECT_DOUBLE_EQ(3.403025, fad_host(1).val()); - EXPECT_DOUBLE_EQ(3.401, fad_host(2).val()); - EXPECT_DOUBLE_EQ(3.403025, fad_host(3).val()); - EXPECT_DOUBLE_EQ(3.4, fad_host(4).val()); - EXPECT_DOUBLE_EQ(3.4025, fad_host(5).val()); - EXPECT_DOUBLE_EQ(3.401, fad_host(6).val()); - EXPECT_DOUBLE_EQ(3.4035, fad_host(7).val()); - - EXPECT_DOUBLE_EQ(0.0, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.4500000000000055, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.4500000000000055, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(4).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(5).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(6).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(7).fastAccessDx(0)); - - EXPECT_DOUBLE_EQ(1.0, fad_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.5499999999999945, fad_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(1.0, fad_host(2).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.5499999999999945, fad_host(3).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(4).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(5).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(1.0, fad_host(6).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(1.0, fad_host(7).fastAccessDx(1)); -} - -//---------------------------------------------------------------------------// -void clampTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 22; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 0.25; - double clamp_lo = -2.125; - double clamp_hi = 4.875; - constexpr int num_value = 11; - Kokkos::Array x_dbl - = {-std::numeric_limits::infinity(), - -100.0, - -2.375, - 5.125, - 100.0, - std::numeric_limits::infinity(), - -1.875, - 2.125, - 4.625, - -2.125, - 4.875}; - Kokkos::Array x_fad; - for (int i = 0; i < num_value; ++i) - { - x_fad[i] = x_dbl[i]; - x_fad[i].diff(0, 1); - } - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_abs", - Kokkos::RangePolicy(0, num_value), - KOKKOS_LAMBDA(const int i) { - dbl_result(i) - = SmoothMath::clamp(x_dbl[i], clamp_lo, clamp_hi, tol); - fad_result(i) - = SmoothMath::clamp(x_fad[i], clamp_lo, clamp_hi, tol); - dbl_result(i + num_value) - = SmoothMath::clamp(x_dbl[i], clamp_lo, clamp_hi, 0.0); - fad_result(i + num_value) - = SmoothMath::clamp(x_fad[i], clamp_lo, clamp_hi, 0.0); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - // Check the value results. - EXPECT_DOUBLE_EQ(-2.125, dbl_host(0)); - EXPECT_DOUBLE_EQ(-2.125, dbl_host(1)); - EXPECT_DOUBLE_EQ(-2.125, dbl_host(2)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(3)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(4)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(5)); - EXPECT_DOUBLE_EQ(-1.875, dbl_host(6)); - EXPECT_DOUBLE_EQ(2.125, dbl_host(7)); - EXPECT_DOUBLE_EQ(4.625, dbl_host(8)); - EXPECT_DOUBLE_EQ(-2.0625, dbl_host(9)); - EXPECT_DOUBLE_EQ(4.8125, dbl_host(10)); - - EXPECT_DOUBLE_EQ(-2.125, dbl_host(11)); - EXPECT_DOUBLE_EQ(-2.125, dbl_host(12)); - EXPECT_DOUBLE_EQ(-2.125, dbl_host(13)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(14)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(15)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(16)); - EXPECT_DOUBLE_EQ(-1.875, dbl_host(17)); - EXPECT_DOUBLE_EQ(2.125, dbl_host(18)); - EXPECT_DOUBLE_EQ(4.625, dbl_host(19)); - EXPECT_DOUBLE_EQ(-2.125, dbl_host(20)); - EXPECT_DOUBLE_EQ(4.875, dbl_host(21)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(-2.125, fad_host(0).val()); - EXPECT_DOUBLE_EQ(-2.125, fad_host(1).val()); - EXPECT_DOUBLE_EQ(-2.125, fad_host(2).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(3).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(4).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(5).val()); - EXPECT_DOUBLE_EQ(-1.875, fad_host(6).val()); - EXPECT_DOUBLE_EQ(2.125, fad_host(7).val()); - EXPECT_DOUBLE_EQ(4.625, fad_host(8).val()); - EXPECT_DOUBLE_EQ(-2.0625, fad_host(9).val()); - EXPECT_DOUBLE_EQ(4.8125, fad_host(10).val()); - - EXPECT_DOUBLE_EQ(-2.125, fad_host(11).val()); - EXPECT_DOUBLE_EQ(-2.125, fad_host(12).val()); - EXPECT_DOUBLE_EQ(-2.125, fad_host(13).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(14).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(15).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(16).val()); - EXPECT_DOUBLE_EQ(-1.875, fad_host(17).val()); - EXPECT_DOUBLE_EQ(2.125, fad_host(18).val()); - EXPECT_DOUBLE_EQ(4.625, fad_host(19).val()); - EXPECT_DOUBLE_EQ(-2.125, fad_host(20).val()); - EXPECT_DOUBLE_EQ(4.875, fad_host(21).val()); - - EXPECT_DOUBLE_EQ(0.0, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(4).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(5).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(6).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(7).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(8).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.5, fad_host(9).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.5, fad_host(10).fastAccessDx(0)); - - EXPECT_DOUBLE_EQ(0.0, fad_host(11).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(12).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(13).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(14).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(15).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(16).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(17).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(18).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.0, fad_host(19).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(20).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(21).fastAccessDx(0)); -} - -//---------------------------------------------------------------------------// -void rampTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 5; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double ramp_start = 0.5; - const double ramp_end = 1.5; - Kokkos::Array x_dbl = {0.45, 0.5, 1.0, 1.5, 1.55}; - Kokkos::Array x_fad; - for (int i = 0; i < num_result; ++i) - { - x_fad[i] = x_dbl[i]; - x_fad[i].diff(0, 1); - } - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_ramp", - Kokkos::RangePolicy(0, num_result), - KOKKOS_LAMBDA(const int i) { - dbl_result(i) = SmoothMath::ramp(x_dbl[i], ramp_start, ramp_end); - fad_result(i) = SmoothMath::ramp(x_fad[i], ramp_start, ramp_end); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - // Check the value results. - EXPECT_DOUBLE_EQ(0.0, dbl_host(0)); - EXPECT_DOUBLE_EQ(0.0, dbl_host(1)); - EXPECT_DOUBLE_EQ(0.5, dbl_host(2)); - EXPECT_DOUBLE_EQ(1.0, dbl_host(3)); - EXPECT_DOUBLE_EQ(1.0, dbl_host(4)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(0.0, fad_host(0).val()); - EXPECT_DOUBLE_EQ(0.0, fad_host(1).val()); - EXPECT_DOUBLE_EQ(0.5, fad_host(2).val()); - EXPECT_DOUBLE_EQ(1.0, fad_host(3).val()); - EXPECT_DOUBLE_EQ(1.0, fad_host(4).val()); - - EXPECT_DOUBLE_EQ(0.0, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(1.5707963267948966, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(4).fastAccessDx(0)); -} - -//---------------------------------------------------------------------------// -void hypot2DTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 9; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 4.0; - const double x_dbl = 1.5; - const double y_dbl = -2.0; - const fad_type x_fad(2, 0, x_dbl); - const fad_type y_fad(2, 1, y_dbl); - - const fad_type x0_fad(2, 0, 0.0); - const fad_type y0_fad(2, 1, 0.0); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_hypot", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::hypot(x_dbl, y_dbl, 0.0); - fad_result(0) = SmoothMath::hypot(x_fad, y_fad, 0.0); - - dbl_result(1) = SmoothMath::hypot(x_dbl, y_dbl, tol); - fad_result(1) = SmoothMath::hypot(x_fad, y_fad, tol); - - dbl_result(2) = SmoothMath::hypot(y_dbl, x_dbl, 0.0); - fad_result(2) = SmoothMath::hypot(y_fad, x_fad, 0.0); - - dbl_result(3) = SmoothMath::hypot(y_dbl, x_dbl, tol); - fad_result(3) = SmoothMath::hypot(y_fad, x_fad, tol); - - dbl_result(4) = SmoothMath::hypot(x_dbl, x_dbl, 0.0); - fad_result(4) = SmoothMath::hypot(x_fad, x_fad, 0.0); - - dbl_result(5) = SmoothMath::hypot(x_dbl, x_dbl, tol); - fad_result(5) = SmoothMath::hypot(x_fad, x_fad, tol); - - dbl_result(6) = SmoothMath::hypot(y_dbl, y_dbl, 0.0); - fad_result(6) = SmoothMath::hypot(y_fad, y_fad, 0.0); - - dbl_result(7) = SmoothMath::hypot(y_dbl, y_dbl, tol); - fad_result(7) = SmoothMath::hypot(y_fad, y_fad, tol); - - dbl_result(8) = SmoothMath::hypot(0.0, 0.0, tol); - fad_result(8) = SmoothMath::hypot(x0_fad, y0_fad, tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - const double sqrt2 = std::sqrt(2); - - // Check the value results. - EXPECT_DOUBLE_EQ(2.5, dbl_host(0)); - EXPECT_DOUBLE_EQ(2.78125, dbl_host(1)); - EXPECT_DOUBLE_EQ(2.5, dbl_host(2)); - EXPECT_DOUBLE_EQ(2.78125, dbl_host(3)); - EXPECT_DOUBLE_EQ(1.5 * sqrt2, dbl_host(4)); - EXPECT_DOUBLE_EQ(2.5625, dbl_host(5)); - EXPECT_DOUBLE_EQ(2.0 * sqrt2, dbl_host(6)); - EXPECT_DOUBLE_EQ(3.0, dbl_host(7)); - EXPECT_DOUBLE_EQ(2.0, dbl_host(8)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(2.5, fad_host(0).val()); - EXPECT_DOUBLE_EQ(2.78125, fad_host(1).val()); - EXPECT_DOUBLE_EQ(2.5, fad_host(2).val()); - EXPECT_DOUBLE_EQ(2.78125, fad_host(3).val()); - EXPECT_DOUBLE_EQ(1.5 * sqrt2, fad_host(4).val()); - EXPECT_DOUBLE_EQ(2.5625, fad_host(5).val()); - EXPECT_DOUBLE_EQ(2.0 * sqrt2, fad_host(6).val()); - EXPECT_DOUBLE_EQ(3.0, fad_host(7).val()); - EXPECT_DOUBLE_EQ(2.0, fad_host(8).val()); - - EXPECT_DOUBLE_EQ(0.6, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.375, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.6, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.375, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(sqrt2, fad_host(4).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.75, fad_host(5).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(6).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(7).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(8).fastAccessDx(0)); - - EXPECT_DOUBLE_EQ(-0.8, fad_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.5, fad_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.8, fad_host(2).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.5, fad_host(3).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(4).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(5).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-sqrt2, fad_host(6).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-1.0, fad_host(7).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(8).fastAccessDx(1)); -} - -//---------------------------------------------------------------------------// -void normTest() -{ - static constexpr int num_dbl_result = 4; - static constexpr int num_dfad_result = 2; - static constexpr int num_sfad_result = 2; - - using dfad_type = Sacado::Fad::DFad; - using sfad_2d_type = Sacado::Fad::SFad; - using sfad_3d_type = Sacado::Fad::SFad; - - // Input vector types - using dbl_view = Kokkos::View; - using dfad_view = Kokkos::View; - using sfad_2d_view = Kokkos::View; - using sfad_3d_view = Kokkos::View; - - // Results view types - using dbl_results_view - = Kokkos::View; - using dfad_results_view - = Kokkos::View; - using sfad_2d_results_view - = Kokkos::View; - using sfad_3d_results_view - = Kokkos::View; - - // Create the double vectors - dbl_view vec_dbl_2d("vec_dbl_2d", 2); - dbl_view vec_dbl_3d("vec_dbl_3d", 3); - - // Create dfad versions. Note the last extra "hidden" dimension must equal - // num_derivs + 1 - dfad_view vec_dfad_2d("vec_dfad_2d", 2, 3); - dfad_view vec_dfad_3d("vec_dfad_3d", 3, 4); - - // Create sfad versions. - sfad_2d_view vec_sfad_2d("vec_dfad_2d"); - sfad_3d_view vec_sfad_3d("vec_dfad_3d"); - - // Create host mirror views for initialization - auto vec_dbl_2d_host = Kokkos::create_mirror_view(vec_dbl_2d); - auto vec_dbl_3d_host = Kokkos::create_mirror_view(vec_dbl_3d); - auto vec_dfad_2d_host = Kokkos::create_mirror_view(vec_dfad_2d); - auto vec_dfad_3d_host = Kokkos::create_mirror_view(vec_dfad_3d); - auto vec_sfad_2d_host = Kokkos::create_mirror_view(vec_sfad_2d); - auto vec_sfad_3d_host = Kokkos::create_mirror_view(vec_sfad_3d); - - // Initialize the vectors - vec_dbl_2d_host(0) = 0.25; - vec_dbl_2d_host(1) = -0.166; - - vec_dbl_3d_host(0) = 0.175; - vec_dbl_3d_host(1) = 0.33; - vec_dbl_3d_host(2) = -0.92; - - // Initialize the fad vectors - vec_dfad_2d_host(0) = dfad_type(2, 0, 0.25); - vec_dfad_2d_host(1) = dfad_type(2, 1, -0.166); - - vec_sfad_2d_host(0) = sfad_2d_type(2, 0, 0.25); - vec_sfad_2d_host(1) = sfad_2d_type(2, 1, -0.166); - - vec_dfad_3d_host(0) = dfad_type(3, 0, 0.175); - vec_dfad_3d_host(1) = dfad_type(3, 1, 0.33); - vec_dfad_3d_host(2) = dfad_type(3, 2, -0.92); - - vec_sfad_3d_host(0) = sfad_3d_type(3, 0, 0.175); - vec_sfad_3d_host(1) = sfad_3d_type(3, 1, 0.33); - vec_sfad_3d_host(2) = sfad_3d_type(3, 2, -0.92); - - // Deep copy the initialized vectors to device - Kokkos::deep_copy(vec_dbl_2d, vec_dbl_2d_host); - Kokkos::deep_copy(vec_dbl_3d, vec_dbl_3d_host); - Kokkos::deep_copy(vec_dfad_2d, vec_dfad_2d_host); - Kokkos::deep_copy(vec_dfad_3d, vec_dfad_3d_host); - Kokkos::deep_copy(vec_sfad_2d, vec_sfad_2d_host); - Kokkos::deep_copy(vec_sfad_3d, vec_sfad_3d_host); - - // Make result views. - dbl_results_view dbl_result("dbl_result"); - - // Note we still need to specify the compile-time dimension size. - dfad_results_view dfad_2d_results("2d_dfad_results", num_dfad_result, 3); - dfad_results_view dfad_3d_results("3d_dfad_results", num_dfad_result, 4); - - // Intialize the dfad-type results views - for (int i = 0; i < num_dfad_result; ++i) - { - dfad_2d_results(i) = 0.0; - dfad_2d_results(i).diff(0, 2); - - dfad_3d_results(i) = 0.0; - dfad_3d_results(i).diff(0, 3); - } - - sfad_2d_results_view sfad_2d_results("2d_sfad_results"); - sfad_3d_results_view sfad_3d_results("3d_sfad_results"); - - const double tol = 1.0; - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_norm", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::norm(vec_dbl_2d, 0.0); - dbl_result(1) = SmoothMath::norm(vec_dbl_2d, tol); - dbl_result(2) = SmoothMath::norm(vec_dbl_3d, 0.0); - dbl_result(3) = SmoothMath::norm(vec_dbl_3d, tol); - - dfad_2d_results(0) = SmoothMath::norm(vec_dfad_2d, 0.0); - dfad_2d_results(1) = SmoothMath::norm(vec_dfad_2d, tol); - - sfad_2d_results(0) = SmoothMath::norm(vec_sfad_2d, 0.0); - sfad_2d_results(1) = SmoothMath::norm(vec_sfad_2d, tol); - - dfad_3d_results(0) = SmoothMath::norm(vec_dfad_3d, 0.0); - dfad_3d_results(1) = SmoothMath::norm(vec_dfad_3d, tol); - - sfad_3d_results(0) = SmoothMath::norm(vec_sfad_3d, 0.0); - sfad_3d_results(1) = SmoothMath::norm(vec_sfad_3d, tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto sfad_2d_host = Kokkos::create_mirror_view_and_copy( - Kokkos::HostSpace{}, sfad_2d_results); - auto sfad_3d_host = Kokkos::create_mirror_view_and_copy( - Kokkos::HostSpace{}, sfad_3d_results); - - // Kokkos create_mirror_view_and_copy gives an error for DFAD types - // when node type is CUDA. Use two-step copy for these Views. - auto dfad_2d_host = Kokkos::create_mirror(dfad_2d_results); - Kokkos::deep_copy(dfad_2d_host, dfad_2d_results); - auto dfad_3d_host = Kokkos::create_mirror(dfad_3d_results); - Kokkos::deep_copy(dfad_3d_host, dfad_3d_results); - - EXPECT_DOUBLE_EQ(0.30009331881932994, dbl_host(0)); - EXPECT_DOUBLE_EQ(0.545028, dbl_host(1)); - EXPECT_DOUBLE_EQ(0.992937560977527, dbl_host(2)); - EXPECT_DOUBLE_EQ(0.9929625, dbl_host(3)); - - EXPECT_DOUBLE_EQ(0.30009331881932994, dfad_2d_host(0).val()); - EXPECT_DOUBLE_EQ(0.8330741949990281, dfad_2d_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.5531612654793547, dfad_2d_host(0).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.30009331881932994, sfad_2d_host(0).val()); - EXPECT_DOUBLE_EQ(0.8330741949990281, sfad_2d_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.5531612654793547, sfad_2d_host(0).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.545028, dfad_2d_host(1).val()); - EXPECT_DOUBLE_EQ(0.25, dfad_2d_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.166, dfad_2d_host(1).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.545028, sfad_2d_host(1).val()); - EXPECT_DOUBLE_EQ(0.25, sfad_2d_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.166, sfad_2d_host(1).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.992937560977527, dfad_3d_host(0).val()); - EXPECT_DOUBLE_EQ(0.17624471757087729, dfad_3d_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.33234718170508293, dfad_3d_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.9265436580868979, dfad_3d_host(0).fastAccessDx(2)); - - EXPECT_DOUBLE_EQ(0.9929625, dfad_3d_host(1).val()); - EXPECT_DOUBLE_EQ(0.175, dfad_3d_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.33, dfad_3d_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.92, dfad_3d_host(1).fastAccessDx(2)); -} - -//---------------------------------------------------------------------------// -void metricNormTest() -{ - static constexpr int num_dbl_result = 8; - static constexpr int num_dfad_result = 4; - - using dfad_type = Sacado::Fad::DFad; - - // Input types - using dfad_vector_view = Kokkos::View; - using dfad_matrix_view = Kokkos::View; - using dbl_matrix_view = Kokkos::View; - using dbl_view = Kokkos::View; - - // Results view types - using dbl_results_view - = Kokkos::View; - using dfad_results_view - = Kokkos::View; - - // Create the inputs for 2d - dfad_vector_view vec_dfad_2d("vec_dfad_2d", 2, 3); - dbl_view vec_dbl_2d("vec_dbl_2d", 2); - dfad_matrix_view identity_dfad_2d("identity_dfad_2d", 2, 2, 3); - dbl_matrix_view identity_dbl_2d("identity_dbl_2d", 2, 2); - dfad_matrix_view metric_dfad_2d("metric_dfad_2d", 2, 2, 3); - dbl_matrix_view metric_dbl_2d("metric_dbl_2d", 2, 2); - - // Create the inputs for 3d - dfad_vector_view vec_dfad_3d("vec_dfad_3d", 3, 4); - dbl_view vec_dbl_3d("vec_dbl_3d", 3); - dfad_matrix_view identity_dfad_3d("identity_dfad_3d", 3, 3, 4); - dbl_matrix_view identity_dbl_3d("identity_dbl_3d", 3, 3); - dfad_matrix_view metric_dfad_3d("metric_dfad_3d", 3, 3, 4); - dbl_matrix_view metric_dbl_3d("metric_dbl_3d", 3, 3); - - // Make result views - dbl_results_view dbl_result("dbl_result"); - dfad_results_view dfad_2d_result("dfad_2d_result", num_dfad_result, 3); - dfad_results_view dfad_3d_result("dfad_3d_result", num_dfad_result, 4); - - // Create host mirror views for initialization - auto vec_dbl_2d_host = Kokkos::create_mirror_view(vec_dbl_2d); - auto vec_dbl_3d_host = Kokkos::create_mirror_view(vec_dbl_3d); - auto identity_dbl_2d_host = Kokkos::create_mirror_view(identity_dbl_2d); - auto identity_dbl_3d_host = Kokkos::create_mirror_view(identity_dbl_3d); - auto metric_dbl_2d_host = Kokkos::create_mirror_view(metric_dbl_2d); - auto metric_dbl_3d_host = Kokkos::create_mirror_view(metric_dbl_3d); - - // Kokkos create_mirror_view_and_copy gives an error for DFAD types - // when node type is CUDA. Use two-step copy for these Views. - auto vec_dfad_2d_host = Kokkos::create_mirror(vec_dfad_2d); - Kokkos::deep_copy(vec_dfad_2d_host, vec_dfad_2d); - auto vec_dfad_3d_host = Kokkos::create_mirror(vec_dfad_3d); - Kokkos::deep_copy(vec_dfad_3d_host, vec_dfad_3d); - auto identity_dfad_2d_host = Kokkos::create_mirror(identity_dfad_2d); - Kokkos::deep_copy(identity_dfad_2d_host, identity_dfad_2d); - auto identity_dfad_3d_host = Kokkos::create_mirror(identity_dfad_3d); - Kokkos::deep_copy(identity_dfad_3d_host, identity_dfad_3d); - auto metric_dfad_2d_host = Kokkos::create_mirror(metric_dfad_2d); - Kokkos::deep_copy(metric_dfad_2d_host, metric_dfad_2d); - auto metric_dfad_3d_host = Kokkos::create_mirror(metric_dfad_3d); - Kokkos::deep_copy(metric_dfad_3d_host, metric_dfad_3d); - - // Identity metrics for 2d and 3d - for (int i = 0; i < 2; ++i) - { - for (int j = 0; j < 2; ++j) - { - identity_dfad_2d_host(i, j) = dfad_type(2, j, 0.0); - identity_dbl_2d_host(i, j) = 0.0; - } - identity_dfad_2d_host(i, i) = dfad_type(2, i, 1.0); - identity_dbl_2d_host(i, i) = 1.0; - } - for (int i = 0; i < 3; ++i) - { - for (int j = 0; j < 3; ++j) - { - identity_dfad_3d_host(i, j) = dfad_type(3, j, 0.0); - identity_dbl_3d_host(i, j) = 0.0; - } - identity_dfad_3d_host(i, i) = dfad_type(3, i, 1.0); - identity_dbl_3d_host(i, i) = 1.0; - } - - // Initialize the fad vectors for 2d and 3d - vec_dfad_2d_host(0) = dfad_type(2, 0, 0.25); - vec_dfad_2d_host(1) = dfad_type(2, 1, -0.166); - - vec_dfad_3d_host(0) = dfad_type(3, 0, 0.175); - vec_dfad_3d_host(1) = dfad_type(3, 1, 0.33); - vec_dfad_3d_host(2) = dfad_type(3, 2, -0.92); - - // Non-trivial metrics for 2d and 3d - metric_dfad_2d_host(0, 0) = dfad_type(2, 0, 0.75); - metric_dfad_2d_host(0, 1) = dfad_type(2, 1, -0.25); - metric_dfad_2d_host(1, 0) = dfad_type(2, 0, -0.25); - metric_dfad_2d_host(1, 1) = dfad_type(2, 1, 0.75); - - metric_dbl_2d_host(0, 0) = 0.75; - metric_dbl_2d_host(0, 1) = -0.25; - metric_dbl_2d_host(1, 0) = -0.25; - metric_dbl_2d_host(1, 1) = 0.75; - - metric_dfad_3d_host(0, 0) = dfad_type(3, 0, 0.375); - metric_dfad_3d_host(0, 1) = dfad_type(3, 1, 0.125); - metric_dfad_3d_host(0, 2) = dfad_type(3, 2, 0.0); - metric_dfad_3d_host(1, 0) = dfad_type(3, 0, 0.125); - metric_dfad_3d_host(1, 1) = dfad_type(3, 1, 0.375); - metric_dfad_3d_host(1, 2) = dfad_type(3, 2, 0.0); - metric_dfad_3d_host(2, 0) = dfad_type(3, 0, 0.0); - metric_dfad_3d_host(2, 1) = dfad_type(3, 1, 0.0); - metric_dfad_3d_host(2, 2) = dfad_type(3, 2, 1.0); - - metric_dbl_3d_host(0, 0) = 0.375; - metric_dbl_3d_host(0, 1) = 0.125; - metric_dbl_3d_host(0, 2) = 0.0; - metric_dbl_3d_host(1, 0) = 0.125; - metric_dbl_3d_host(1, 1) = 0.375; - metric_dbl_3d_host(1, 2) = 0.0; - metric_dbl_3d_host(2, 0) = 0.0; - metric_dbl_3d_host(2, 1) = 0.0; - metric_dbl_3d_host(2, 2) = 1.0; - - // Deep copy the initialized data to device - Kokkos::deep_copy(vec_dbl_2d, vec_dbl_2d_host); - Kokkos::deep_copy(vec_dbl_3d, vec_dbl_3d_host); - Kokkos::deep_copy(vec_dfad_2d, vec_dfad_2d_host); - Kokkos::deep_copy(vec_dfad_3d, vec_dfad_3d_host); - Kokkos::deep_copy(identity_dbl_2d, identity_dbl_2d_host); - Kokkos::deep_copy(identity_dbl_3d, identity_dbl_3d_host); - Kokkos::deep_copy(identity_dfad_2d, identity_dfad_2d_host); - Kokkos::deep_copy(identity_dfad_3d, identity_dfad_3d_host); - Kokkos::deep_copy(metric_dbl_2d, metric_dbl_2d_host); - Kokkos::deep_copy(metric_dbl_3d, metric_dbl_3d_host); - Kokkos::deep_copy(metric_dfad_2d, metric_dfad_2d_host); - Kokkos::deep_copy(metric_dfad_3d, metric_dfad_3d_host); - - // Intialize the dfad-type results views - for (int i = 0; i < num_dfad_result; ++i) - { - dfad_2d_result(i) = 0.0; - dfad_2d_result(i).diff(0, 2); - - dfad_3d_result(i) = 0.0; - dfad_3d_result(i).diff(0, 3); - } - - // Tolerance - const double tol = 1.0; - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_norm", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::norm(vec_dbl_2d, identity_dbl_2d, 0.0); - dbl_result(1) = SmoothMath::norm(vec_dbl_2d, identity_dbl_2d, tol); - dbl_result(2) = SmoothMath::norm(vec_dbl_3d, identity_dbl_3d, 0.0); - dbl_result(3) = SmoothMath::norm(vec_dbl_3d, identity_dbl_3d, tol); - - dbl_result(4) = SmoothMath::norm(vec_dbl_2d, metric_dbl_2d, 0.0); - dbl_result(5) = SmoothMath::norm(vec_dbl_2d, metric_dbl_2d, tol); - dbl_result(6) = SmoothMath::norm(vec_dbl_3d, metric_dbl_3d, 0.0); - dbl_result(7) = SmoothMath::norm(vec_dbl_3d, metric_dbl_3d, tol); - - dfad_2d_result(0) - = SmoothMath::norm(vec_dfad_2d, identity_dbl_2d, 0.0); - dfad_2d_result(1) - = SmoothMath::norm(vec_dfad_2d, identity_dbl_2d, tol); - dfad_2d_result(2) - = SmoothMath::norm(vec_dfad_2d, metric_dbl_2d, 0.0); - dfad_2d_result(3) - = SmoothMath::norm(vec_dfad_2d, metric_dbl_2d, tol); - - dfad_3d_result(0) - = SmoothMath::norm(vec_dfad_3d, identity_dbl_3d, 0.0); - dfad_3d_result(1) - = SmoothMath::norm(vec_dfad_3d, identity_dbl_3d, tol); - dfad_3d_result(2) - = SmoothMath::norm(vec_dfad_3d, metric_dbl_3d, 0.0); - dfad_3d_result(3) - = SmoothMath::norm(vec_dfad_3d, metric_dbl_3d, tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - - // Kokkos create_mirror_view_and_copy gives an error for DFAD types - // when node type is CUDA. Use two-step copy for these Views. - auto dfad_2d_host = Kokkos::create_mirror(dfad_2d_result); - Kokkos::deep_copy(dfad_2d_host, dfad_2d_result); - auto dfad_3d_host = Kokkos::create_mirror(dfad_3d_result); - Kokkos::deep_copy(dfad_3d_host, dfad_3d_result); - - EXPECT_DOUBLE_EQ(0.300093318819329935, dfad_2d_host(0).val()); - EXPECT_DOUBLE_EQ(0.833074194999028128, dfad_2d_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.553161265479354736, dfad_2d_host(0).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.545028, dfad_2d_host(1).val()); - EXPECT_DOUBLE_EQ(0.25, dfad_2d_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.166, dfad_2d_host(1).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.29713969778540195, dfad_2d_host(2).val()); - EXPECT_DOUBLE_EQ(0.7706812711554505, dfad_2d_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.6293336144369835, dfad_2d_host(2).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.544146, dfad_2d_host(3).val()); - EXPECT_DOUBLE_EQ(0.22899999999999998, dfad_2d_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(-0.18699999999999997, dfad_2d_host(3).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.992937560977526945, dfad_3d_host(0).val()); - EXPECT_DOUBLE_EQ(0.176244717570877285, dfad_3d_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.332347181705082928, dfad_3d_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.926543658086897870, dfad_3d_host(0).fastAccessDx(2)); - - EXPECT_DOUBLE_EQ(0.9929625, dfad_3d_host(1).val()); - EXPECT_DOUBLE_EQ(0.175, dfad_3d_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.33, dfad_3d_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.92, dfad_3d_host(1).fastAccessDx(2)); - - EXPECT_DOUBLE_EQ(0.9555937290501649, dfad_3d_host(2).val()); - EXPECT_DOUBLE_EQ(0.11184146227731206, dfad_3d_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.1523921678983258, dfad_3d_host(2).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.962752236679552, dfad_3d_host(2).fastAccessDx(2)); - - EXPECT_DOUBLE_EQ(0.9565796875000001, dfad_3d_host(3).val()); - EXPECT_DOUBLE_EQ(0.10687499999999998, dfad_3d_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.14562499999999998, dfad_3d_host(3).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.92, dfad_3d_host(3).fastAccessDx(2)); -} - -//---------------------------------------------------------------------------// -void hypot3DTest() -{ - using fad_type = Sacado::Fad::SFad; - - // Make result views. - constexpr int num_result = 13; - Kokkos::View dbl_result("dbl_result"); - Kokkos::View fad_result( - "fad_result"); - - // Setup test values. - const double tol = 16.0; - const double x_dbl = 3.0; - const double y_dbl = -4.0; - const double z_dbl = 12.0; - const fad_type x_fad(3, 0, x_dbl); - const fad_type y_fad(3, 1, y_dbl); - const fad_type z_fad(3, 2, z_dbl); - - const fad_type x0_fad(3, 0, 0.0); - const fad_type y0_fad(3, 1, 0.0); - const fad_type z0_fad(3, 2, 0.0); - - // Apply the operation in a kernel. - Kokkos::parallel_for( - "smooth_hypot", - Kokkos::RangePolicy(0, 1), - KOKKOS_LAMBDA(const int) { - dbl_result(0) = SmoothMath::hypot(x_dbl, y_dbl, z_dbl, 0.0); - fad_result(0) = SmoothMath::hypot(x_fad, y_fad, z_fad, 0.0); - - dbl_result(1) = SmoothMath::hypot(x_dbl, y_dbl, z_dbl, tol); - fad_result(1) = SmoothMath::hypot(x_fad, y_fad, z_fad, tol); - - dbl_result(2) = SmoothMath::hypot(y_dbl, x_dbl, z_dbl, 0.0); - fad_result(2) = SmoothMath::hypot(y_fad, x_fad, z_fad, 0.0); - - dbl_result(3) = SmoothMath::hypot(y_dbl, x_dbl, z_dbl, tol); - fad_result(3) = SmoothMath::hypot(y_fad, x_fad, z_fad, tol); - - dbl_result(4) = SmoothMath::hypot(y_dbl, z_dbl, x_dbl, 0.0); - fad_result(4) = SmoothMath::hypot(y_fad, z_fad, x_fad, 0.0); - - dbl_result(5) = SmoothMath::hypot(y_dbl, z_dbl, x_dbl, tol); - fad_result(5) = SmoothMath::hypot(y_fad, z_fad, x_fad, tol); - - dbl_result(6) = SmoothMath::hypot(x_dbl, x_dbl, x_dbl, 0.0); - fad_result(6) = SmoothMath::hypot(x_fad, x_fad, x_fad, 0.0); - - dbl_result(7) = SmoothMath::hypot(x_dbl, x_dbl, x_dbl, tol); - fad_result(7) = SmoothMath::hypot(x_fad, x_fad, x_fad, tol); - - dbl_result(8) = SmoothMath::hypot(y_dbl, y_dbl, y_dbl, 0.0); - fad_result(8) = SmoothMath::hypot(y_fad, y_fad, y_fad, 0.0); - - dbl_result(9) = SmoothMath::hypot(y_dbl, y_dbl, y_dbl, tol); - fad_result(9) = SmoothMath::hypot(y_fad, y_fad, y_fad, tol); - - dbl_result(10) = SmoothMath::hypot(z_dbl, z_dbl, z_dbl, 0.0); - fad_result(10) = SmoothMath::hypot(z_fad, z_fad, z_fad, 0.0); - - dbl_result(11) = SmoothMath::hypot(z_dbl, z_dbl, z_dbl, 3.0 * tol); - fad_result(11) = SmoothMath::hypot(z_fad, z_fad, z_fad, 3.0 * tol); - - dbl_result(12) = SmoothMath::hypot(0.0, 0.0, 0.0, tol); - fad_result(12) = SmoothMath::hypot(x0_fad, y0_fad, z0_fad, tol); - }); - - // Copy results to host. - auto dbl_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, dbl_result); - auto fad_host - = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, fad_result); - - const double sqrt3 = std::sqrt(3); - - // Check the value results. - EXPECT_DOUBLE_EQ(13.0, dbl_host(0)); - EXPECT_DOUBLE_EQ(13.28125, dbl_host(1)); - EXPECT_DOUBLE_EQ(13.0, dbl_host(2)); - EXPECT_DOUBLE_EQ(13.28125, dbl_host(3)); - EXPECT_DOUBLE_EQ(13.0, dbl_host(4)); - EXPECT_DOUBLE_EQ(13.28125, dbl_host(5)); - EXPECT_DOUBLE_EQ(3.0 * sqrt3, dbl_host(6)); - EXPECT_DOUBLE_EQ(8.84375, dbl_host(7)); - EXPECT_DOUBLE_EQ(4.0 * sqrt3, dbl_host(8)); - EXPECT_DOUBLE_EQ(9.5, dbl_host(9)); - EXPECT_DOUBLE_EQ(12.0 * sqrt3, dbl_host(10)); - EXPECT_DOUBLE_EQ(28.5, dbl_host(11)); - EXPECT_DOUBLE_EQ(8.0, dbl_host(12)); - - // Check the derivative results. - EXPECT_DOUBLE_EQ(13.0, fad_host(0).val()); - EXPECT_DOUBLE_EQ(13.28125, fad_host(1).val()); - EXPECT_DOUBLE_EQ(13.0, fad_host(2).val()); - EXPECT_DOUBLE_EQ(13.28125, fad_host(3).val()); - EXPECT_DOUBLE_EQ(13.0, fad_host(4).val()); - EXPECT_DOUBLE_EQ(13.28125, fad_host(5).val()); - EXPECT_DOUBLE_EQ(3.0 * sqrt3, fad_host(6).val()); - EXPECT_DOUBLE_EQ(8.84375, fad_host(7).val()); - EXPECT_DOUBLE_EQ(4.0 * sqrt3, fad_host(8).val()); - EXPECT_DOUBLE_EQ(9.5, fad_host(9).val()); - EXPECT_DOUBLE_EQ(12.0 * sqrt3, fad_host(10).val()); - EXPECT_DOUBLE_EQ(28.5, fad_host(11).val()); - - EXPECT_DOUBLE_EQ(0.23076923076923078, fad_host(0).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.1875, fad_host(1).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.23076923076923078, fad_host(2).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.1875, fad_host(3).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.23076923076923078, fad_host(4).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.1875, fad_host(5).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(sqrt3, fad_host(6).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.5625, fad_host(7).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(8).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(9).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(10).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(11).fastAccessDx(0)); - EXPECT_DOUBLE_EQ(0.0, fad_host(12).fastAccessDx(0)); - - EXPECT_DOUBLE_EQ(-0.30769230769230771, fad_host(0).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.25, fad_host(1).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.30769230769230771, fad_host(2).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.25, fad_host(3).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.30769230769230771, fad_host(4).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.25, fad_host(5).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(6).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(7).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-sqrt3, fad_host(8).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(-0.75, fad_host(9).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(10).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(11).fastAccessDx(1)); - EXPECT_DOUBLE_EQ(0.0, fad_host(12).fastAccessDx(1)); - - EXPECT_DOUBLE_EQ(0.92307692307692313, fad_host(0).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.75, fad_host(1).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.92307692307692313, fad_host(2).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.75, fad_host(3).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.92307692307692313, fad_host(4).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.75, fad_host(5).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.0, fad_host(6).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.0, fad_host(7).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.0, fad_host(8).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.0, fad_host(9).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(sqrt3, fad_host(10).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.75, fad_host(11).fastAccessDx(2)); - EXPECT_DOUBLE_EQ(0.0, fad_host(12).fastAccessDx(2)); -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(SmoothMath, smooth_abs) -{ - absTest(); -} - -TEST(SmoothMath, smooth_min) -{ - minTest(); -} - -TEST(SmoothMath, smooth_max) -{ - maxTest(); -} - -TEST(SmoothMath, smooth_clamp) -{ - clampTest(); -} - -TEST(SmoothMath, smooth_ramp) -{ - rampTest(); -} - -TEST(SmoothMath, smooth_hypot_2d) -{ - hypot2DTest(); -} - -TEST(SmoothMath, smooth_hypot_3d) -{ - hypot3DTest(); -} - -TEST(SmoothMath, smooth_norm) -{ - normTest(); -} - -TEST(SmoothMath, smooth_metric_norm) -{ - metricNormTest(); -} -//---------------------------------------------------------------------------// - -} // end namespace Test diff --git a/src/utils/unit_test/tstTypeTraits.cpp b/src/utils/unit_test/tstTypeTraits.cpp deleted file mode 100644 index d8e3dfe..0000000 --- a/src/utils/unit_test/tstTypeTraits.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "VertexCFD_Utils_TypeTraits.hpp" - -#include - -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -// Test ExpectedType against ResultType. -template -void testResultType(const Types&...) -{ - using Result = ResultType; - ::testing::StaticAssertTypeEq(); -} - -//---------------------------------------------------------------------------// -// Test with a single argument of different types. -TEST(TypeTraits, OneArgument) -{ - // int - testResultType(1); - - // double - testResultType(1.0); - - // Static FAD - using sfad_type = Sacado::Fad::SFad; - sfad_type a = 1.0; - - // SFad - testResultType(a); - - // SFad expr - testResultType(a * a + 1); - - // Dynamic FAD - using dfad_type = Sacado::Fad::DFad; - dfad_type x(1, 1.0); - - // DFad - testResultType(x); - - // DFad expr - testResultType(x * x + 1); - - // Nested FAD -> DFad - using nfad_type = Sacado::Fad::DFad; - nfad_type p(1, 1.0); - - // DFad - testResultType(p); - - // DFad expr - testResultType(p * p + 1); - - // DFad/DFad/SFad expr - testResultType(a * x + p); -} - -//---------------------------------------------------------------------------// -// Test with two arguments of various type combinations. -TEST(TypeTraits, TwoArgument) -{ - // int / int - testResultType(1, 2); - - // double / int - testResultType(1.0, 2); - testResultType(1, 2.0); - - // double / double - testResultType(1.0, 2.0); - - // Static FAD - using sfad_type = Sacado::Fad::SFad; - sfad_type a = 1.0; - sfad_type b = 2.0; - - // SFad / int - testResultType(a, 1); - testResultType(1, a); - - // SFad / double - testResultType(a, 1.0); - testResultType(1.0, a); - - // SFad / SFad - testResultType(a, b); - - // SFad expr / int - testResultType(a * a + 1, 1); - testResultType(1, a * a + 1); - - // SFad expr / double - testResultType(a * a + 1, 1.0); - testResultType(1.0, a * a + 1); - - // SFad expr / SFad - testResultType(a * a + 1, b); - testResultType(b, a * a + 1); - - // SFad expr / SFad expr - testResultType(a * a + 1, 2 * b + b); - - // Dynamic FAD - using dfad_type = Sacado::Fad::DFad; - dfad_type x(1, 1.0); - dfad_type y(1, 1.0); - - // DFad / int - testResultType(x, 1); - testResultType(1, x); - - // DFad / double - testResultType(x, 1.0); - testResultType(1.0, x); - - // DFad / DFad - testResultType(x, y); - - // DFad expr / int - testResultType(x * x + 1, 1); - testResultType(1, x * x + 1); - - // DFad expr / double - testResultType(x * x + 1, 1.0); - testResultType(1.0, x * x + 1); - - // DFad expr / DFad - testResultType(x * x + 1, y); - testResultType(y, x * x + 1); - - // DFad expr / DFad expr - testResultType(x * x + 1, 2 * y + y); - - // Nested FAD -> DFad - using nfad_type = Sacado::Fad::DFad; - nfad_type p(1, 1.0); - nfad_type q(1, 2.0); - - // DFad / int - testResultType(p, 1); - testResultType(1, p); - - // DFad / double - testResultType(p, 1.0); - testResultType(1.0, p); - - // DFad / SFad - testResultType(p, a); - testResultType(a, p); - - // DFad / SFad expr - testResultType(p, a * a + 1); - testResultType(a * a + 1, p); - - // DFad / DFad - testResultType(p, x); - testResultType(x, p); - - // DFad / DFad expr - testResultType(p, x * x + 1); - testResultType(x * x + 1, p); - - // DFad / DFad - testResultType(p, q); - - // DFad expr / int - testResultType(p * p + 1, 1); - testResultType(1, p * p + 1); - - // DFad expr / double - testResultType(p * p + 1, 1.0); - testResultType(1.0, p * p + 1); - - // DFad expr / SFad - testResultType(p * p + 1, a); - testResultType(a, p * p + 1); - - // DFad expr / SFad expr - testResultType(p * p + 1, 2 * a + 1); - testResultType(2 * a + 1, p * p + 1); - - // DFad expr / DFad - testResultType(p * p + 1, x); - testResultType(x, p * p + 1); - - // DFad expr / DFad expr - testResultType(p * p + 1, 2 * x + 1); - testResultType(2 * x + 1, p * p + 1); - - // DFad expr / DFad - testResultType(p * p + 1, q); - testResultType(q, p * p + 1); - - // DFad expr / DFad expr - testResultType(p * p + 1, 2 * q + q); -} - -//---------------------------------------------------------------------------// -// Test with many arguments of various types. -TEST(TypeTraits, ManyArgument) -{ - // int - testResultType(1, 2, 3, 4, 5); - - // double / int - testResultType(1, 2.0, 3, 4.0, 5); - - // Static FAD - using sfad_type = Sacado::Fad::SFad; - sfad_type a = 1.0; - sfad_type b = 2.0; - - // SFad expr / SFad / double / int - testResultType(1, a, 2.0, a * b + 1, b); - - // Dynamic FAD - using dfad_type = Sacado::Fad::DFad; - dfad_type x(1, 1.0); - dfad_type y(1, 1.0); - - // DFad expr / DFad / double / int - testResultType(1, x, 2.0, x * y + 1, y); - - // Nested FAD -> DFad - using nfad_type = Sacado::Fad::DFad; - nfad_type p(1, 1.0); - nfad_type q(1, 2.0); - - // Everything - testResultType( - 1, p, 2.0, p * q + 1, 2 * x + y, x, a, a * b - 1, a + x + p); -} - -//---------------------------------------------------------------------------// -} // namespace Test diff --git a/src/utils/unit_test/tstVectorizeOutputFieldNames.cpp b/src/utils/unit_test/tstVectorizeOutputFieldNames.cpp deleted file mode 100644 index 155dcde..0000000 --- a/src/utils/unit_test/tstVectorizeOutputFieldNames.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "VertexCFD_Utils_VectorizeOutputFieldNames.hpp" - -#include - -#include - -using namespace VertexCFD; - -namespace Test -{ -//---------------------------------------------------------------------------// -void vectorizeOutputFieldNamesTest() -{ - Teuchos::ParameterList params; - params.sublist("Nodal Quantities") - .set("block_0", "node_val 0,node_val 1,all_val 0") - .set("block_1", "node_val 1,node_val 2,all_val 1"); - params.sublist("Cell Quantities") - .set("block_0", "cell_val 0,cell_val 1,all_val 0") - .set("block_1", "cell_val 1,cell_val 2,all_val 2"); - params.sublist("Cell Average Quantities") - .set("block_0", "cell_avg_val 0,cell_avg_val 1,all_val 3") - .set("block_1", "cell_avg_val 1,cell_avg_val 2,all_val 1"); - params.sublist("Cell Average Vectors") - .set("block_0", "cell_vec_val 0,cell_vec_val 1,all_val 0") - .set("block_1", "cell_vec_val 1,cell_vec_val 2,all_val 3"); - - // Expected returns are vectors of unique field names, which will - // be ordered - const std::vector exp_fields = {"all_val 0", - "all_val 1", - "all_val 2", - "all_val 3", - "cell_avg_val 0", - "cell_avg_val 1", - "cell_avg_val 2", - "cell_val 0", - "cell_val 1", - "cell_val 2", - "node_val 0", - "node_val 1", - "node_val 2"}; - const std::vector exp_vec_fields = {"all_val 0", - "all_val 3", - "cell_vec_val 0", - "cell_vec_val 1", - "cell_vec_val 2"}; - - std::vector out_fields; - std::vector out_vector_fields; - VectorizeOutputFieldNames::getOutputFields( - params, out_fields, out_vector_fields); - - const int size_ef = exp_fields.size(); - const int size_of = out_fields.size(); - EXPECT_EQ(size_ef, size_of); - for (int i = 0; i < size_ef; ++i) - { - EXPECT_EQ(exp_fields[i], out_fields[i]); - } - - const int size_evf = exp_vec_fields.size(); - const int size_ovf = out_vector_fields.size(); - EXPECT_EQ(size_evf, size_ovf); - for (int i = 0; i < size_evf; ++i) - { - EXPECT_EQ(exp_vec_fields[i], out_vector_fields[i]); - } -} - -//---------------------------------------------------------------------------// -// RUN TESTS -//---------------------------------------------------------------------------// -TEST(VectorizeOutputFieldNames, test) -{ - vectorizeOutputFieldNamesTest(); -} - -//---------------------------------------------------------------------------// - -} // namespace Test diff --git a/src/utils/unit_test/tstVersion.cpp b/src/utils/unit_test/tstVersion.cpp deleted file mode 100644 index 67adeb6..0000000 --- a/src/utils/unit_test/tstVersion.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include - -#include - -#include - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -namespace Test -{ -TEST(vertexcfd_version, version_test) -{ - auto const version_id = VertexCFD::Utils::version(); - EXPECT_TRUE(!version_id.empty()); - std::cout << "VertexCFD version " << version_id << std::endl; - - auto const commit_hash = VertexCFD::Utils::git_commit_hash(); - EXPECT_TRUE(!commit_hash.empty()); - std::cout << "VertexCFD commit hash " << commit_hash << std::endl; -} - -} // end namespace Test From d4f31803f5bca62a8cb26218b6264179a0d9101d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 11:49:02 -0500 Subject: [PATCH 112/214] upgrade --- cmake/FindCLANG_FORMAT.cmake | 52 --------------- cmake/FindGCOVR.cmake | 24 ------- cmake/FindSuperLU_dist.cmake | 28 -------- cmake/FindYAPF.cmake | 36 ---------- cmake/VertexCFDConfig.cmake.in | 9 --- paper/paper.bib | 116 --------------------------------- paper/paper.md | 72 -------------------- paper/paper.md.save | 50 -------------- 8 files changed, 387 deletions(-) delete mode 100644 cmake/FindCLANG_FORMAT.cmake delete mode 100644 cmake/FindGCOVR.cmake delete mode 100644 cmake/FindSuperLU_dist.cmake delete mode 100644 cmake/FindYAPF.cmake delete mode 100644 cmake/VertexCFDConfig.cmake.in delete mode 100644 paper/paper.bib delete mode 100644 paper/paper.md delete mode 100644 paper/paper.md.save diff --git a/cmake/FindCLANG_FORMAT.cmake b/cmake/FindCLANG_FORMAT.cmake deleted file mode 100644 index 50a58d4..0000000 --- a/cmake/FindCLANG_FORMAT.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# Find clang-format -# -# CLANG_FORMAT_EXECUTABLE - Path to clang-format executable -# CLANG_FORMAT_FOUND - True if the clang-format executable was found. -# CLANG_FORMAT_VERSION - The version of clang-format found -# - -find_program(CLANG_FORMAT_EXECUTABLE - NAMES clang-format - clang-format-10 - clang-format-9 - clang-format-8 - clang-format-7 - clang-format-6.0 - clang-format-5.0 - clang-format-4.0 - clang-format-3.9 - clang-format-3.8 - clang-format-3.7 - clang-format-3.6 - clang-format-3.5 - clang-format-3.4 - clang-format-3.3 - DOC "clang-format executable") -mark_as_advanced(CLANG_FORMAT_EXECUTABLE) - -# Extract version from command "clang-format -version" -if(CLANG_FORMAT_EXECUTABLE) - execute_process(COMMAND ${CLANG_FORMAT_EXECUTABLE} -version - OUTPUT_VARIABLE clang_format_version - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(clang_format_version MATCHES "^clang-format version .*") - # clang_format_version sample: "clang-format version 3.9.1-4ubuntu3~16.04.1 - # (tags/RELEASE_391/rc2)" - string(REGEX - REPLACE "clang-format version ([.0-9]+).*" - "\\1" - CLANG_FORMAT_VERSION - "${clang_format_version}") - # CLANG_FORMAT_VERSION sample: "3.9.1" - else() - set(CLANG_FORMAT_VERSION 0.0) - endif() -else() - set(CLANG_FORMAT_VERSION 0.0) -endif() - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set CLANG_FORMAT_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(CLANG_FORMAT REQUIRED_VARS CLANG_FORMAT_EXECUTABLE VERSION_VAR CLANG_FORMAT_VERSION) diff --git a/cmake/FindGCOVR.cmake b/cmake/FindGCOVR.cmake deleted file mode 100644 index d47dd6c..0000000 --- a/cmake/FindGCOVR.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# Find gcovr -# -# GCOVR_EXECUTABLE - Path to gcovr executable -# GCOVR_FOUND - True if the gcovr executable was found -# GCOVR_VERSION - The version of gcovr found -# - -find_program(GCOVR_EXECUTABLE - NAMES gcovr - DOC "gcovr executable") -mark_as_advanced(GCOVR_EXECUTABLE) - -# Extract version from command "gcovr -version" -if(GCOVR_EXECUTABLE) - execute_process(COMMAND ${GCOVR_EXECUTABLE} --version - OUTPUT_VARIABLE gcovr_version - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(gcovr_version MATCHES "^gcovr ([.0-9]+)") - set(GCOVR_VERSION ${CMAKE_MATCH_1}) - endif() -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GCOVR REQUIRED_VARS GCOVR_EXECUTABLE VERSION_VAR GCOVR_VERSION) diff --git a/cmake/FindSuperLU_dist.cmake b/cmake/FindSuperLU_dist.cmake deleted file mode 100644 index 300fa86..0000000 --- a/cmake/FindSuperLU_dist.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# Find SuperLU_dist -# SuperLU_dist_FOUND - True if SuperLU_dist was found -# SuperLU_dist::SuperLU_dist - interface target - -find_path(SuperLU_dist_INCLUDE_DIR superlu_ddefs.h) -find_library(SuperLU_dist_LIBRARY NAMES superlu_dist) - -mark_as_advanced( - SuperLU_dist_INCLUDE_DIR - SuperLU_dist_LIBRARY -) - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args( - SuperLU_dist DEFAULT_MSG - SuperLU_dist_LIBRARY SuperLU_dist_INCLUDE_DIR -) - -if(SuperLU_dist_FOUND AND NOT TARGET SuperLU_dist::SuperLU_dist) - add_library(SuperLU_dist::SuperLU_dist UNKNOWN IMPORTED) - set_target_properties(SuperLU_dist::SuperLU_dist PROPERTIES - IMPORTED_LOCATION "${SuperLU_dist_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SuperLU_dist_INCLUDE_DIR}" - ) - message("SuperLU_dist_LIBRARY: ${SuperLU_dist_LIBRARY}") - message("SuperLU_dist_INCLUDE_DIR: ${SuperLU_dist_INCLUDE_DIR}") -endif() diff --git a/cmake/FindYAPF.cmake b/cmake/FindYAPF.cmake deleted file mode 100644 index c595e6c..0000000 --- a/cmake/FindYAPF.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# Find yapf -# -# YAPF_EXECUTABLE - Path to python-format executable -# YAPF_FOUND - True if the python-format executable was found. -# YAPF_VERSION - The version of python-format found -# - -find_program(YAPF_EXECUTABLE - NAMES yapf - DOC "Python formatter executable") -mark_as_advanced(YAPF_EXECUTABLE) - -# Extract version from command "yapf -version" -if(YAPF_EXECUTABLE) - execute_process(COMMAND ${YAPF_EXECUTABLE} -version - OUTPUT_VARIABLE yapf_version - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(yapf_version MATCHES "^yapf .*") - # yapf_version sample: "yapf 0.32.0" - string(REGEX - REPLACE "yapf version ([.0-9]+).*" - "\\1" - YAPF_VERSION - "${yapf_version}") - # YAPF_VERSION sample: "0.32.0" - else() - set(YAPF_VERSION 0.0) - endif() -else() - set(YAPF_VERSION 0.0) -endif() - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set YAPF_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(YAPF REQUIRED_VARS YAPF_EXECUTABLE VERSION_VAR YAPF_VERSION) diff --git a/cmake/VertexCFDConfig.cmake.in b/cmake/VertexCFDConfig.cmake.in deleted file mode 100644 index 6571362..0000000 --- a/cmake/VertexCFDConfig.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -include(CMakeFindDependencyMacro) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}" ) -list(APPEND CMAKE_PREFIX_PATH @CMAKE_PREFIX_PATH@) -find_dependency( Trilinos 13 REQUIRED COMPONENTS - Kokkos - Teuchos - SEACASIoss - STKMesh - STKIO ) diff --git a/paper/paper.bib b/paper/paper.bib deleted file mode 100644 index 6122053..0000000 --- a/paper/paper.bib +++ /dev/null @@ -1,116 +0,0 @@ -@Manual{trilinos-website, -title = {{T}rilinos {P}roject {W}ebsite}, -author = {{T}rilinos {P}roject {T}eam}}, -year = {2020 (acccessed May 22, 2020)}, -url = {https://trilinos.github.io} -} - -@article{Clausen2013, - title = {Entropically damped form of artificial compressibility for explicit simulation of incompressible flow}, - author = {Clausen, Jonathan R.}, - journal = {Phys. Rev. E}, - volume = {87}, - issue = {1}, - pages = {013309}, - numpages = {12}, - year = {2013}, - month = {Jan}, - publisher = {American Physical Society}, - doi = {10.1103/PhysRevE.87.013309}, - url = {https://link.aps.org/doi/10.1103/PhysRevE.87.013309} -} - -@misc{olcf-web, - title = {OLCF}, - note = {https://www.olcf.ornl.gov/}, - url = {https://www.olcf.ornl.gov/}, - year = {2024} -} - -@article{nicoud:hal-00910373, - TITLE = {{Subgrid-scale stress modelling based on the square of the velocity gradient tensor}}, - AUTHOR = {Nicoud, Franck and Ducros, Fr{\'e}d{\'e}ric}, - URL = {https://hal.science/hal-00910373}, - JOURNAL = {{Flow, Turbulence and Combustion}}, - PUBLISHER = {{Springer Verlag}}, - VOLUME = {62}, - NUMBER = {3}, - PAGES = {183-200}, - YEAR = {1999}, - DOI = {10.1023/A:1009995426001}, - KEYWORDS = {large eddy simulations ; wall-bounded flow ; unstructured mesh ; transition ; LARGE-EDDY SIMULATION ; NUMERICAL-SIMULATION ; TURBULENCE ; FLOW}, - PDF = {https://hal.science/hal-00910373v1/file/paper.pdf}, - HAL_ID = {hal-00910373}, - HAL_VERSION = {v1}, -} - -@article{kokkos, - author={Trott, Christian R. and Lebrun-Grandié, Damien and Arndt, Daniel and Ciesko, Jan and Dang, Vinh and Ellingwood, Nathan and Gayatri, Rahulkumar and Harvey, Evan and Hollman, Daisy S. and Ibanez, Dan and Liber, Nevin and Madsen, Jonathan and Miles, Jeff and Poliakoff, David and Powell, Amy and Rajamanickam, Sivasankaran and Simberg, Mikael and Sunderland, Dan and Turcksin, Bruno and Wilke, Jeremiah}, - journal={IEEE Transactions on Parallel and Distributed Systems}, - title={Kokkos 3: Programming Model Extensions for the Exascale Era}, - year={2022}, - volume={33}, - number={4}, - pages={805-817}, - doi={10.1109/TPDS.2021.3097283}} - -# Verification -# TGV -@article{Taylor-green-vortex, - ISSN = {00804630}, - URL = {http://www.jstor.org/stable/96892}, - author = {G. I. Taylor and A. E. Green}, - journal = {Proceedings of the Royal Society of London. Series A, Mathematical and Physical Sciences}, - number = {895}, - pages = {499--521}, - publisher = {The Royal Society}, - title = {Mechanism of the Production of Small Eddies from Large Ones}, - urldate = {2023-08-29}, - volume = {158}, - year = {1937} -} - -# Validation -# Natural convection -@article{Kuehn_Goldstein_1976, title={An experimental and theoretical study of natural convection in the annulus between horizontal concentric cylinders}, volume={74}, DOI={10.1017/S0022112076002012}, number={4}, journal={Journal of Fluid Mechanics}, author={Kuehn, T. H. and Goldstein, R. J.}, year={1976}, pages={695–719}}

- -# Flow around a circle -@article{tritton_1959, title={Experiments on the flow past a circular cylinder at low Reynolds numbers}, volume={6}, DOI={10.1017/S0022112059000829}, number={4}, journal={Journal of Fluid Mechanics}, publisher={Cambridge University Press}, author={Tritton, D. J.}, year={1959}, pages={547–567}} - -# Flow around a blunt -@article{10.1115/1.3240731, - author = {Lane, J. C. and Loehrke, R. I.}, - title = "{Leading Edge Separation From a Blunt Plate at Low Reynolds Number}", - journal = {Journal of Fluids Engineering}, - volume = {102}, - number = {4}, - pages = {494-496}, - year = {1980}, - month = {12}, - abstract = "{The flow over a blunt plate aligned parallel to the stream was visualized using dye tracers. A leading edge separation bubble was observed to form at a Reynolds number based on plate thickness of 100. The steady, laminar separation bubble on a long plate, L/t ≥ 8, grows in size with increasing Reynolds number reaching a maximum streamwise length at Ret = 325. The separated shear layer becomes unsteady and the bubble shrinks in size with further increases in Reynolds number. The leading and trailing edge separation zones on short plates, L/t ≤ 4, may combine to form a large recirculation pocket.}", - issn = {0098-2202}, - doi = {10.1115/1.3240731}, - url = {https://doi.org/10.1115/1.3240731}, - eprint = {https://asmedigitalcollection.asme.org/fluidsengineering/article-pdf/102/4/494/5531330/494\_1.pdf}, -} - -@article{SMOLENTSEV201565, -title = {An approach to verification and validation of MHD codes for fusion applications}, -journal = {Fusion Engineering and Design}, -volume = {100}, -pages = {65-72}, -year = {2015}, -issn = {0920-3796}, -doi = {https://doi.org/10.1016/j.fusengdes.2014.04.049}, -url = {https://www.sciencedirect.com/science/article/pii/S0920379614003263}, -author = {S. Smolentsev and S. Badia and R. Bhattacharyay and L. Bühler and L. Chen and Q. Huang and H.-G. Jin and D. Krasnov and D.-W. Lee and E. Mas {de les Valls} and C. Mistrangelo and R. Munipalli and M.-J. Ni and D. Pashkevich and A. Patel and G. Pulugundla and P. Satyamurthy and A. Snegirev and V. Sviridov and P. Swain and T. Zhou and O. Zikanov}, -keywords = {Blanket, Liquid metal magnetohydrodynamics, Computer code}, -abstract = {We propose a new activity on verification and validation (V&V) of MHD codes presently employed by the fusion community as a predictive capability tool for liquid metal cooling applications, such as liquid metal blankets. The important steps in the development of MHD codes starting from the 1970s are outlined first and then basic MHD codes, which are currently in use by designers of liquid breeder blankets, are reviewed. A benchmark database of five problems has been proposed to cover a wide range of MHD flows from laminar fully developed to turbulent flows, which are of interest for fusion applications: (A) 2D fully developed laminar steady MHD flow, (B) 3D laminar, steady developing MHD flow in a non-uniform magnetic field, (C) quasi-two-dimensional MHD turbulent flow, (D) 3D turbulent MHD flow, and (E) MHD flow with heat transfer (buoyant convection). Finally, we introduce important details of the proposed activities, such as basic V&V rules and schedule. The main goal of the present paper is to help in establishing an efficient V&V framework and to initiate benchmarking among interested parties. The comparison results computed by the codes against analytical solutions and trusted experimental and numerical data as well as code-to-code comparisons will be presented and analyzed in companion paper/papers.} -} - -@misc{nasa-web, - title = {Turbulence Modeling Resource}, - note = {Langley Research Center}, - url = {https://turbmodels.larc.nasa.gov/}, - year = {2024} -} \ No newline at end of file diff --git a/paper/paper.md b/paper/paper.md deleted file mode 100644 index 6d74b3e..0000000 --- a/paper/paper.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: 'VERTEX-CFD: A multiphysics platform for fusion applications' -tags: - - Computational Fluid Dynamics - - CPU and GPU - - Finite Element Method -authors: - - name: Marco Delchini - affiliation: 1 - corresponding: true - - name: Kellis Kincaid - affiliation: 1 - - name: Furkan Oz - affiliation: 1 - - name: Jason DeGraw - affiliation: 2 - - name: Kalyan Gottiparthi - affiliation: 3 - - name: Doug Stefanski - affiliation: 4 - - name: Ryan Glasby - affiliation: 4 - - name: Stuart Slattery - affiliation: 4 - - name: Franklin Stuart - affiliation: 5 -affiliations: - - name: Nuclear Energy and Fuel Cycle Division, Oak Ridge National Laboratory - index: 1 - - name: Building and Transportation Division, Oak Ridge National Laboratory - index: 2 - - name: National Center for Computation Science Division, Oak Ridge National Laboratory - index: 3 - - name: Computational Science and Engineering Division, Oak Ridge National Laboratory - index: 4 - - name: Enrichment Science and Engineering Division, Oak Ridge National Laboratory - index: 5 -date: 19 December 2024 -bibliography: paper.bib ---- - -# Summary: - -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to effort in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on CPU and GPU computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory is developed to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, and fully-implicit monolithic solvers. Special attention is being paid during the development process to verify and to validate the solver, and to ensure performance portability across both CPU and GPU computing platforms. A comprehensive verification and validation (V&V) suite and unit tests were designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems taken from the published literature. - -# Statement of need - -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) [@trilinos-website]. The VERTEX-CFD solver is designed to scale and to be compatible with various CPU and GPU architectures on HPC platforms by leveraging Kokkos [@kokkos] programming language. - - -# Current capabilities and development workflow - -VERTEX-CFD solver is still under active development and currently implements the following capabilities: incompressible Navier-Stokes equations [@Clausen2013], temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) [@nicoud:hal-00910373] turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against benchmark problems taken from the published literature: isothermal flows [@Taylor-green-vortex, @10.1115/1.3240731; @Clausen2013], heated flows [@Kuehn_Goldstein_1976; @tritton_1959], transient and steady-state cases, turbulent cases [@nicoud:hal-00910373; @nasa-web], and MHD flows [@SMOLENTSEV201565]. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). - -The long term objectives of the VERTEX initiative is to facilitate the addition of new physical models by relying on a plug-and-play architecture, and also guarantee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and additions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. - -# Conclusions - -VERTEX-CFD is an open-source CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and MHD equation. Reynolds Averaged Navier-Stokes (RANS) turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. VERTEX-CFD solver was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit [@olcf-web]) architectures. - -Future development will focus on implementing wall functions for RANS models, and adding conjugate heat transfer capabilities. - - -# Acknowledgements - -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) program. - - -# Disclaimer - -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the [DOE Public Access Plan](http://energy.gov/downloads/doe-public-access-plan). diff --git a/paper/paper.md.save b/paper/paper.md.save deleted file mode 100644 index bcfaccc..0000000 --- a/paper/paper.md.save +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: 'VERTEX-CFD: A multiphysics platform for fusion applications' -tags: - - C++ - - Trilinos - - Computational Fluid Dynamics - - Kokkos -authors: - - name: - given-names: Marc-Olivier - surname: Delchini - - name: - given-names: Kellis - surname: Kincaid - -date: 19 December 2024 -bibliography: paper.bib - -# Summary: - -The demand for high-performance computational fluid dynamics and multiphysics software packages has grown in recent years as a response to efforts in complex engineering and research applications. While the widespread deployment of high-performance computing (HPC) resources has enabled larger, more complex simulations to be conducted, few commercial or open-source software packages are available which scale performantly on various computing architectures, and represent the multitude of physical processes relevant to these applications. The VERTEX initiative at Oak Ridge National Laboratory was created to address this technical gap, with a special emphasis on high-fidelity multiphysics modeling of coupled turbulent fluid flow, heat transfer, and magnetohydrodynamics for applications in fusion and fission energy, isotope separation and enrichment, and other spaces. The VERTEX-CFD module was developed to solve the governing equations of these problems using a high-order continuous Galerkin finite element framework, employing an artificial compressiblity method for pressure-velocity coupling and fully-implicit monolithic solvers. Special attention was paid during the development process to ensure performance portability across both CPU and GPU computing platforms. In this work, we present the results of a comprehensive verification and validation (V\&V) suite designed to assess the accuracy and convergence behavior of the VERTEX-CFD module for problems involving laminar, heated flows. Comparisons were made to closed-form analytical solutions as well as experiments to ensure both numerical and physical accuracy. The performance and scaling behavior of the software was examined for a representative problem on both CPU and GPU architectures, up to the scale of thousands of compute nodes. These analyses demonstrate the capabilities of the VERTEX-CFD, build trust in the solver accuracy, and provide the basis for future work. - - -# Statement of need - -The core work of the cross-cutting VERTEX Laboratory Directed Research and Development (LDRD) initiative aims to create a new multiphysics simulation framework supporting physical phenomena key to Oak Ridge National Laboratory (ORNL) mission-critical challenges. ORNL has clearly demonstrated needs in modeling and simulation of gas dynamics, rarefied flow, plasma-surface interaction, electromagnetics, magneto-hydrodynamics (MHD), and thermal hydraulics for conducting fluids, collisionless and collisional plasma, and structural mechanics. The project is organized into four technical areas: VERTEX-CORE, VERTEX-MAXWELL, VERTEX-CFD, and VERTEX-CLOSURE. Each area focuses on a specific physics while relying on the common interface VERTEX-CORE. -As part of the VERTEX initiative, the primary mission of the VERTEX-CFD team is to develop modeling and simulation capabilities to accurately model the physics in fusion blanket design. It thus requires a multiphysics solver to implement the incompressible Navier-Stokes (NS) equation to conjugate a heat transfer model and an MHD solver. Solvers, finite element methods, and other relevant tools are provided by the [Trilinos package](https://trilinos.github.io/) \cite{trilinos-website}. The VERTEX-CFD solver is designed to scale on HPC platforms by leveraging Kokkos programming language to ensure compatibility with various CPU and GPU architectures. -The long term objectives of the VERTEX initiative is to faciliate the addiiton of new physical models by relying on a plug-and-play architecture, and also guarentee the correctness of the implemented model over time. New physics and equations are easily added to the global tree and allow for quick deployment of new physical model on HPC platforms. Such approach can only be made possible by setting clear requirements and review process for all developers contributing to the project code: any changes and aditions to the source code is reviewed and tested before being merged. VERTEX-CFD solver is tested daily on a continuous integration (CI) workflow that is hosted on ORNL network. - -# Current capabilities -VERTEX-CFD solver is still under active development and currently implement the following capabilities: incompressible Navier-Stokes equations, temperature equation, induction-less and full-induction MHD models, RANS turbulence models and WALE (LES) turbulence model. Each new physics is implemented in closure models with unit tests. Physical models and coupling between equations were verified and validated against bechmark problems taken from the published literature: isothermal flows, heated flows, transient and steady-state cases, turbulent cases. VERTEX-CFD solver has demonstrated second-order temporal and spatial accuracy. Scaling of the VERTEX-CFD solver was assessed on CPUs and GPUs architecture. It was found that strong and weak scaling were comparable to other CFD solvers alike NekRS. (ADD FIGURE). - -# Mathematics - -# Conclusions -VERTEX-CFD is a CFD solver that relies on a finite element discretization method to solve for the incompressible Navier-Stokes equations coupled to a temperature equation and an electric potential equation. Reynolds Averaged Navier-Stokes turbulence models and large eddy simulation model are also available. The code relies on the Trilinos package and offers a wide range of temporal integrators, solvers and preconditioners to run on CPU- and GPU-enabled platforms. The code was verified and validated for steady and unsteady incompressible flows with benchmark cases taken from the published literature: natural convection, viscous heating, laminar flow over a circle, and turbulent channels. It was also demonstrated that VERTEX-CFD solver scales on CPUs (Perlmutter) and GPUs (Perlmutter and Summit) architectures. - -Current and future work include addition of a conjugate heat transfer (CHT) model, conjugate electric transfer model, and deployment of VERTEX-CFD on Frontier \cite{summit2018} for testing and optimization of the solver on AMD GPUs. - -# Acknolegements -This work was funded by the Laboratory Directed Research and Development (LDRD) program at Oak Ridge National Laboratory, and the Scientific Discovery through Advanced Computing (SciDac) programm. - -# Disclaimer -This manuscript has been authored by UT-Battelle, LLC, under contract DE-AC05-00OR22725 with the US Department of Energy (DOE). The US government retains and the publisher, by accepting the article for publication, acknowledges that the US government retains a nonexclusive, paid-up, irrevocable, worldwide license to publish or reproduce the published form of this manuscript, or allow others to do so, for US government purposes. DOE will provide public access to these results of federally sponsored research in accordance with the DOE Public Access Plan (http://energy.gov/downloads/doe-public-access-plan). - -## [CPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-cpu.md) - -## [GPU BUILD INSTRUCTIONS](docs/install-vertexcfd/install-vertexcfd-on-narsil-gpu.md) - -## [RUNNING CASES WITH VERTEX-CFD](docs/run-vertexcfd/run-incompressible-channel.md) From 8c95b692bcc9d8f2e7bcaf13922cb2202c8bd97e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 11:50:10 -0500 Subject: [PATCH 113/214] upgrade --- .github/workflows/draft-pdf.yml | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .github/workflows/draft-pdf.yml diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml deleted file mode 100644 index 1410492..0000000 --- a/.github/workflows/draft-pdf.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Draft PDF -on: [push] -jobs: - paper: - runs-on: ubuntu-latest - name: Paper Draft - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build draft PDF - uses: openjournals/openjournals-draft-action@master - with: - journal: joss - # This should be the path to the paper within your repo. - paper-path: paper/paper.md - - name: Upload - uses: actions/upload-artifact@v4 - with: - name: paper - # This is the output path where Pandoc will write the compiled - # PDF. Note, this should be the same directory as the input - # paper.md - path: paper/paper.pdf From dfdd61e525a2a8c89527b89fde69e5092c26221b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:27:20 -0500 Subject: [PATCH 114/214] upgrade --- index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.md b/index.md index e800587..edb9da0 100644 --- a/index.md +++ b/index.md @@ -5,6 +5,8 @@ nav_order: 1 permalink: / --- +image + An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } @@ -22,6 +24,7 @@ We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how - [Furkan Oz](https://www.ornl.gov/staff-profile/furkan-oz) - [Kalyan Gottiparthi](https://www.ornl.gov/staff-profile/kalyan-c-gottiparthi) - [Jason Degraw](https://www.ornl.gov/staff-profile/jason-w-degraw) +- [Doug Stefanski](https://www.ornl.gov/staff-profile/douglas-l-stefanski) - [Filipe Leite Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) ## Citing From e088526251645e4648a913efbeee89fd945eb8ed Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:29:31 -0500 Subject: [PATCH 115/214] upgrade --- index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/index.md b/index.md index edb9da0..9c382a0 100644 --- a/index.md +++ b/index.md @@ -6,6 +6,7 @@ permalink: / --- image +image An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From e97fb13a362f9b9be0dc17af16c7fe205780e3fa Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:33:10 -0500 Subject: [PATCH 116/214] upgrade --- index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index 9c382a0..0faaddc 100644 --- a/index.md +++ b/index.md @@ -5,8 +5,8 @@ nav_order: 1 permalink: / --- -image -image +#image +image An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From 4e34bc40a2ec43729bf49f3d73b69eeafcc3dac2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:35:53 -0500 Subject: [PATCH 117/214] upgrade --- index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/index.md b/index.md index 0faaddc..e7a6619 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,6 @@ nav_order: 1 permalink: / --- -#image image An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. From 725dedab8bd990e2cc0cdd327e474dc117ef526f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:39:16 -0500 Subject: [PATCH 118/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index e7a6619..83b4d18 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ nav_order: 1 permalink: / --- -image +[Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png "a title") An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From f16593fe234238a232e9c5f61a4e9fbec3ae9361 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:41:09 -0500 Subject: [PATCH 119/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 83b4d18..1c5132e 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ nav_order: 1 permalink: / --- -[Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png "a title") +![Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png "a title") An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From 30258ee998c0077c43ab172eaa25485e5fc33ba2 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:43:01 -0500 Subject: [PATCH 120/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 1c5132e..96fe66d 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ nav_order: 1 permalink: / --- -![Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png "a title") +![Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png?raw=true) An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From 1689fe38e6ebdaaea360ceb52f56c4f8f331ce24 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:47:35 -0500 Subject: [PATCH 121/214] upgrade --- index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.md b/index.md index 96fe66d..37f6620 100644 --- a/index.md +++ b/index.md @@ -7,6 +7,8 @@ permalink: / ![Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png?raw=true) +Description of the image + An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From e36d4477bcd50d9c61caa137f323541c48dd6a92 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:49:15 -0500 Subject: [PATCH 122/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 37f6620..dba07b5 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ nav_order: 1 permalink: / --- -![Alt text](https://github.com/ORNL/VERTEX-CFD/blob/gh-pages/docs/figures/vertex_cfd_heated_flow_logo.png?raw=true) +![Alt text](docs/figures/vertex_cfd_heated_flow_logo.png) Description of the image From 3f9576dc16d1e0e7e0dd48e85173d09f3316272b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:51:53 -0500 Subject: [PATCH 123/214] upgrade --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index dba07b5..4b7b1ae 100644 --- a/index.md +++ b/index.md @@ -7,7 +7,7 @@ permalink: / ![Alt text](docs/figures/vertex_cfd_heated_flow_logo.png) -Description of the image +Description of the image An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From 02e8a1a7d154aa5b42446a6c1c9be1855522f856 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:53:46 -0500 Subject: [PATCH 124/214] upgrade --- index.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.md b/index.md index 4b7b1ae..96c73f1 100644 --- a/index.md +++ b/index.md @@ -7,8 +7,6 @@ permalink: / ![Alt text](docs/figures/vertex_cfd_heated_flow_logo.png) -Description of the image - An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } From 7ca0ab5c32fec25cbf8d56c22cbcb10f67a70c85 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 31 Jan 2025 15:57:38 -0500 Subject: [PATCH 125/214] upgrade --- docs/theory.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 4413572..0e46558 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,6 +11,11 @@ usemathjax: true ## The physics +\begin{align} + \delta_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ + \delta_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} +\end{align} + ## The discretized equations ## Boundary conditions From b470ba3ac729a3959fd24f6d0cd2a865b0b632eb Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:09:24 -0500 Subject: [PATCH 126/214] upgrade --- docs/theory.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 0e46558..c1f4af1 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,10 +11,12 @@ usemathjax: true ## The physics +$$ \begin{align} \delta_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ \delta_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} \end{align} +$$ ## The discretized equations From 54b7a3b690393241e6091ed7840f6203bb9e65d5 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:16:06 -0500 Subject: [PATCH 127/214] upgrade --- docs/theory.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index c1f4af1..b144ad9 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -9,12 +9,16 @@ usemathjax: true --- -## The physics +VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs \cite{delchini4972920vertex}. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos \cite{heroux2012new} and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package \cite{kokkos}. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. + +## Governing equations + +VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ \begin{align} - \delta_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ - \delta_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} + \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ + \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} \end{align} $$ From 974b12e4a3481e10780efd474f9784c57eb2ef4b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:19:24 -0500 Subject: [PATCH 128/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index b144ad9..311a12d 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -9,7 +9,7 @@ usemathjax: true --- -VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs \cite{delchini4972920vertex}. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos \cite{heroux2012new} and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package \cite{kokkos}. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. +VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. ## Governing equations From 53e926e1f4a89309cb3d7468b27022ff2cd756da Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:21:00 -0500 Subject: [PATCH 129/214] upgrade --- docs/theory.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 311a12d..7e6bfb9 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -8,8 +8,7 @@ usemathjax: true # Theory --- - -VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. +Test ## Governing equations @@ -22,6 +21,8 @@ $$ \end{align} $$ +VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. + ## The discretized equations ## Boundary conditions From 7c350476ccb0fcde046107b193374589cbbaf2bd Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:22:14 -0500 Subject: [PATCH 130/214] upgrade --- docs/theory.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 7e6bfb9..dac8db5 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -8,7 +8,7 @@ usemathjax: true # Theory --- -Test +VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. ## Governing equations @@ -21,7 +21,6 @@ $$ \end{align} $$ -VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. ## The discretized equations From 45c895d6da4bec547aae7c3f3592112f20241e78 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:24:57 -0500 Subject: [PATCH 131/214] upgrade --- docs/theory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index dac8db5..dfcd83c 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -8,10 +8,11 @@ usemathjax: true # Theory --- -VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. ## Governing equations +VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. + VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ @@ -21,7 +22,6 @@ $$ \end{align} $$ - ## The discretized equations ## Boundary conditions From 714aba8381445a9076ff441e84c7c3147e19be0b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Sat, 1 Feb 2025 14:26:02 -0500 Subject: [PATCH 132/214] upgrade --- docs/theory.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index dfcd83c..bedb9a2 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,8 +11,6 @@ usemathjax: true ## Governing equations -VERTEX-CFD is a computational fluid dynamics (CFD) solver being developed at Oak Ridge National Laboratory to model fusion blanket designs. This multiphysics problem requires a robust and fast solver that implements the incompressible Navier--Stokes equations, heat transfer and conjugate heat transfer, turbulence models, and magneto-hydrodynamic equations. The VERTEX-CFD solver relies on Trilinos and its subsequent package for high-order temporal integrators and its discretization method and solver options. Both CPU and GPU hardware are supported with the Kokkos templated C++ package. Because of the multiphysics aspect of the project, which involves a wide range of spatial and temporal scales, Trilinos was chosen to rely on an implicit solver and to select numerical methods that scale well on high-performance computing systems. - VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ From 3bf84f03744e9fc193c57aeb948d059414b449b3 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:05:43 -0500 Subject: [PATCH 133/214] upgrade --- docs/theory.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index bedb9a2..66b9d78 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -16,12 +16,26 @@ VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature $$ \begin{align} \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ - \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} + \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} +\end{align} +$$ + +$$ +\begin{align}\label{eq:final-inductionless-mhd} +\left\{ +\begin{matrix} + \nabla \cdot \mathbf{u} = 0 \\ + \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} - \rho \mathbf{g} \beta (T - T_0) \\ + \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. +\end{matrix} +\right. \end{align} $$ ## The discretized equations +The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. + ## Boundary conditions ## Initial conditions From 6d9a68227cfd2d9331b5bcc02cfe1ca06e984a22 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:09:23 -0500 Subject: [PATCH 134/214] upgrade --- docs/theory.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 66b9d78..bfdc177 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -20,18 +20,6 @@ $$ \end{align} $$ -$$ -\begin{align}\label{eq:final-inductionless-mhd} -\left\{ -\begin{matrix} - \nabla \cdot \mathbf{u} = 0 \\ - \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} - \rho \mathbf{g} \beta (T - T_0) \\ - \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. -\end{matrix} -\right. -\end{align} -$$ - ## The discretized equations The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. From 9ce15adaa2b9492df0faa6253610de8adef6c0c6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:10:55 -0500 Subject: [PATCH 135/214] upgrade --- docs/theory.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index bfdc177..16886ec 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,10 +14,11 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ -\begin{align} \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ +$$ + +$$ \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} -\end{align} $$ ## The discretized equations From a6f6c7eee722ab021d02c6e35669e9fff712cb69 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:13:39 -0500 Subject: [PATCH 136/214] upgrade --- docs/theory.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 16886ec..4750ece 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,11 +14,19 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ - \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ +\partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ $$ $$ - \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} +\partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} +$$ + +$$ +\nabla \cdot (\mathbf{u}) = 0 +$$ + +$$ +\rho\left(\frac{\partial \mathbf{u}}{\partial t} + \mathbf{u} \cdot \nabla \mathbf{u}\right) = \nabla \cdot (\mu \nabla \mathbf{u}) - \nabla p + \rho_k \mathbf{g} - D\mathbf{u} $$ ## The discretized equations From 0ccdaaab4014bd2862026fc13e3446d0c2134ac1 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:15:10 -0500 Subject: [PATCH 137/214] upgrade --- docs/theory.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 4750ece..530bda7 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,20 +14,11 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ +\nabla \cdot (\mathbf{u}) = 0 \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ -$$ - -$$ \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} $$ -$$ -\nabla \cdot (\mathbf{u}) = 0 -$$ - -$$ -\rho\left(\frac{\partial \mathbf{u}}{\partial t} + \mathbf{u} \cdot \nabla \mathbf{u}\right) = \nabla \cdot (\mu \nabla \mathbf{u}) - \nabla p + \rho_k \mathbf{g} - D\mathbf{u} -$$ ## The discretized equations From e6a33a5ca7d515a3e4ea7bcce46deebe80daac69 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:16:35 -0500 Subject: [PATCH 138/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 530bda7..4f27dd7 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,7 +14,7 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ -\nabla \cdot (\mathbf{u}) = 0 +\nabla \cdot (\vec{u}) &= 0 \partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ \partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} $$ From e476340826eb905f9965a6e8715a84a19a37154a Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:18:32 -0500 Subject: [PATCH 139/214] upgrade --- docs/theory.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 4f27dd7..d1dabc6 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -13,16 +13,12 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). -$$ -\nabla \cdot (\vec{u}) &= 0 -\partial_t \vec{u} + \vec{u} \cdot \nabla \vec{u} &= - \nabla p + \nu \nabla^2 \vec{u} + \vec{f}_b \tag{1} \\ -\partial_t T + \vec{u} \cdot \nabla T &= \alpha \nabla^2 T + S_T \tag{2} -$$ - ## The discretized equations -The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. +The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. + +VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs) ## Boundary conditions From 71be74e17a903fb7949e232afe7c334aea76c598 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:24:00 -0500 Subject: [PATCH 140/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index d1dabc6..d61caf9 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -18,7 +18,7 @@ VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. -VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs) +VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. ## Boundary conditions From bc95011443315b1b74d249a071b51bceca46470a Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:29:08 -0500 Subject: [PATCH 141/214] upgrade --- docs/theory.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index d61caf9..e6c9300 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -13,6 +13,18 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). + $$ +\begin{align} +\left\{ +\begin{matrix} + \nabla \cdot \mathbf{u} = 0 \\ + \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} - \rho \mathbf{g} \beta (T - T_0) \\ + \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. +\end{matrix} +\right. +\end{align} +$$ + ## The discretized equations @@ -22,4 +34,6 @@ VERTEX-CFD employs a finite element discretization method and high-order implici ## Boundary conditions +Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. + ## Initial conditions From 0fe96b271dfc161d5718e1f24b89c480e948850c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:33:28 -0500 Subject: [PATCH 142/214] upgrade --- docs/theory.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index e6c9300..4e13001 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -19,7 +19,9 @@ VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} - \rho \mathbf{g} \beta (T - T_0) \\ - \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. + \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. \\ + \nabla \cdot \mathbf{J} = 0 \\ + \mathbf{J} = \nabla(\sigma \mathbf{u} \cross \mathbf{B}) \end{matrix} \right. \end{align} From e429aeb91063e21b9e3e8221181745d85caaee06 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:35:45 -0500 Subject: [PATCH 143/214] upgrade --- docs/theory.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 4e13001..5b2add1 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -13,15 +13,14 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). - $$ -\begin{align} +$$ \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ - \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} - \rho \mathbf{g} \beta (T - T_0) \\ - \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''}. \\ - \nabla \cdot \mathbf{J} = 0 \\ - \mathbf{J} = \nabla(\sigma \mathbf{u} \cross \mathbf{B}) + \partial_t \mathbf{u} + (\mathbf{u} \cdot \nabla) \mathbf{u} = -\frac{1}{\rho}\nabla P + \nu \Delta \mathbf{u} + \frac{1}{\rho}\mathbf{J} \times \mathbf{B^0} - \mathbf{g} \beta (T - T_0) \\ + \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''} \\ + \mathbf{J} = \sigma ( -\nabla \varphi + \mathbf{u} \times \mathbf{B^0} ) \\ + \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] \end{matrix} \right. \end{align} From 84f3a28be8bce02cc4d27f3652cd068c71c4f827 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:37:12 -0500 Subject: [PATCH 144/214] upgrade --- docs/theory.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 5b2add1..c0d3704 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,6 +14,7 @@ usemathjax: true VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). $$ +\begin{align} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -26,6 +27,20 @@ $$ \end{align} $$ +$$ +\begin{align} +\left\{ +\begin{matrix} + \nabla \cdot \mathbf{u} = 0 \\ + \partial_t \rho \mathbf{u} + \rho (\mathbf{u} \cdot \nabla) \mathbf{u} = -\nabla P + \rho \nu \Delta \mathbf{u} + f^L - \rho \mathbf{g} \beta (T - T_0) \\ + f^L = \mathbf{J} \times \mathbf{B^0} = \sigma \left( -\nabla \varphi \times \mathbf{B^0} + (\mathbf{B} \cdot \mathbf{u}) \cdot \mathbf{B^0} - ||\mathbf{B^0}||^2 \mathbf{u} \right) \\ + \rho C_p \left( \partial_t T + \mathbf{u} \cdot \nabla T \right) = \nabla \cdot (k \nabla T ) + q^{'''} \\ + \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] +\end{matrix} +\right. +\end{align} +$$ + ## The discretized equations From adb0292bf1fc426fefb5bfb2145d9ebc20abc7b8 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:41:29 -0500 Subject: [PATCH 145/214] upgrade --- docs/theory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index c0d3704..983761f 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,7 +11,7 @@ usemathjax: true ## Governing equations -VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). +VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). Coupling between the different equations is ensured by the Buoyancy force and the Lorentz force. $$ \begin{align} @@ -27,6 +27,8 @@ $$ \end{align} $$ +The equations are recast in a conservative form and solved for the pressure $$P$$, the velocity $$\mathbf{u}$$, the temperature $$T$$, and the electric potential $$\varphi$$. + $$ \begin{align} \left\{ @@ -44,8 +46,6 @@ $$ ## The discretized equations -The governing equations are discretized with a finite element method (FEM). The resulting ordinary differential equations (ODEs) are integrated with a fully implicit temporal integrators from the Tempus package. - VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. ## Boundary conditions From 733474f16cbaf7acfc858d42d1a791d0d4a14ed0 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:55:50 -0500 Subject: [PATCH 146/214] upgrade --- docs/theory.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 983761f..67c9338 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -50,6 +50,28 @@ VERTEX-CFD employs a finite element discretization method and high-order implici ## Boundary conditions -Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. +Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. The boundary conditions implemented in VERTEX-CFD are listed below: + +The boundary conditions implemented in VERTEX-CFD are listed below: + +- **Periodic boundary** (Section 1.1) +- **Dirichlet with time-transient variation** (Section 1.2) +- **Symmetry for isothermal flow** (Section 1.3) +- **No-slip for viscous flow** (Section 1.4) +- **Rotating wall for isothermal flow** (Section 1.5) +- **Laminar flow** (Section 1.6) +- **Outflow with back pressure** (Section 1.7) +- **Conducting and isolating wall** + +\begin{itemize} + \item Periodic boundary (Section \ref{sec:periodic-bc}). + \item Dirichlet with time-transient variation (Section \ref{sec:dirichlet-bc}). + \item Symmetry for isothermal flow (Section \ref{sec:symm-bc}). + \item No-slip for viscous flow (Section \ref{sec:viscous-flow-bc}). + \item Rotating wall for isothermal flow (Section \ref{sec:rotating-wall-bc}). + \item Laminar flow (Section \ref{sec:laminar-flow}). + \item Outflow with back pressure (Section \ref{sec:back-press-bc}). + \item Conducting and isolating wall +\end{itemize} ## Initial conditions From ecb3bed682b1b2338110ad544fffc005213b46af Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:57:04 -0500 Subject: [PATCH 147/214] upgrade --- docs/theory.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 67c9338..3c8ea2b 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -63,6 +63,7 @@ The boundary conditions implemented in VERTEX-CFD are listed below: - **Outflow with back pressure** (Section 1.7) - **Conducting and isolating wall** +$$ \begin{itemize} \item Periodic boundary (Section \ref{sec:periodic-bc}). \item Dirichlet with time-transient variation (Section \ref{sec:dirichlet-bc}). @@ -73,5 +74,6 @@ The boundary conditions implemented in VERTEX-CFD are listed below: \item Outflow with back pressure (Section \ref{sec:back-press-bc}). \item Conducting and isolating wall \end{itemize} +$$ ## Initial conditions From 73d19adfc3ad2a016d22832041eb507efa0ae58b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 14:59:41 -0500 Subject: [PATCH 148/214] upgrade --- docs/theory.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 3c8ea2b..91c9850 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -48,32 +48,23 @@ $$ VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. + ## Boundary conditions Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. The boundary conditions implemented in VERTEX-CFD are listed below: The boundary conditions implemented in VERTEX-CFD are listed below: -- **Periodic boundary** (Section 1.1) -- **Dirichlet with time-transient variation** (Section 1.2) -- **Symmetry for isothermal flow** (Section 1.3) -- **No-slip for viscous flow** (Section 1.4) -- **Rotating wall for isothermal flow** (Section 1.5) -- **Laminar flow** (Section 1.6) -- **Outflow with back pressure** (Section 1.7) +- **Periodic boundary** +- **Dirichlet with time-transient variation** +- **Symmetry for isothermal flow** +- **No-slip for viscous flow** +- **Rotating wall for isothermal flow** +- **Laminar flow** +- **Outflow with back pressure** - **Conducting and isolating wall** -$$ -\begin{itemize} - \item Periodic boundary (Section \ref{sec:periodic-bc}). - \item Dirichlet with time-transient variation (Section \ref{sec:dirichlet-bc}). - \item Symmetry for isothermal flow (Section \ref{sec:symm-bc}). - \item No-slip for viscous flow (Section \ref{sec:viscous-flow-bc}). - \item Rotating wall for isothermal flow (Section \ref{sec:rotating-wall-bc}). - \item Laminar flow (Section \ref{sec:laminar-flow}). - \item Outflow with back pressure (Section \ref{sec:back-press-bc}). - \item Conducting and isolating wall -\end{itemize} -$$ +The vector solution is denoted by $$U_{bc} = (\phi_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. + ## Initial conditions From e8f91450736df9eddd15dc9b11c447c83e024167 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 21:55:31 -0500 Subject: [PATCH 149/214] upgrade --- docs/theory.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 91c9850..19201e0 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -66,5 +66,47 @@ The boundary conditions implemented in VERTEX-CFD are listed below: The vector solution is denoted by $$U_{bc} = (\phi_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. +### Dirichlet boundary +The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $T_{bc}$. Linear ramping in time is also available and can be used to vary each primitive variable independently. + +$$ +\begin{equation} +\left\{ \ +\begin{matrix} + u_{i,bc}(\mathbf{r}, t) = u_{i,D}(t) \\ + \phi_{p,{bc}}(\mathbf{r}, t) = \phi_p(\mathbf{r}, t) \\ + \partial_i U_{bc}(\mathbf{r}, t) = \partial_i U(\mathbf{r}, t) \\ + T_{bc}(\mathbf{r}, t) = T_{D} +\end{matrix} +\right. +\end{equation} +$$ + +### Symmetry boundary condition +The symmetry boundary condition is a no-penetration condition. The normal component of the fluid velocity to the wall is zero while the tangential component is unrestricted. The same observation is valid for the temeprature gradient as well. Assuming the outward normal vector to a wall boundary is denoted by $$\mathbf{n}_{bc} = \left(n_{bc,x}, n_{bc,y}, n_{bc,z} \right)$$ in a three-dimensional computational domain, the boundary condition for the primitive variables reads: + +$$ +\begin{equation} +\left\{ +\begin{matrix} + \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + \mathbf{u}_{bc}(\mathbf{r}, t) &=& \mathbf{u}(\mathbf{r}, t) - \left( \mathbf{u}(\mathbf{r}, t) \cdot \mathbf{n}_{bc} \right) \mathbf{n_{bc}} \\ + T_{bc}(\mathbf{r}, t) &=& T(\mathbf{r}, t) \\ + \varphi_{bc}(\mathbf{r}, t) &=& \varphi(\mathbf{r}, t) +\end{matrix} +\right. +\end{equation} +$$ + +The boundary gradients are function of the interior values: + +$$ +\begin{align} + \partial_i U_{bc}(\mathbf{r}, t) =& \partial_i U(\mathbf{r}, t) \nonumber\\ + \partial_i T_{bc}(\mathbf{r}, t) =& \partial_i T(\mathbf{r}, t) - \left(\partial_i T(\mathbf{r}, t) \cdot \mathbf{n}_{bc} \right) n_{i} \\ + \partial_i \varphi_{bc}(\mathbf{r}, t) =& \partial_i \varphi(\mathbf{r}, t) - \left(\partial_i \varphi(\mathbf{r}, t) \cdot \mathbf{n}_{bc} \right) n_{i} \nonumber +\end{align} +$$ + ## Initial conditions From 3fdbc7d764d33cfd3e6884a6f8f99f6683aa56fe Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 21:59:50 -0500 Subject: [PATCH 150/214] upgrade --- docs/theory.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index 19201e0..d383adf 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -108,5 +108,64 @@ $$ \end{align} $$ +### No-slip boundary condition +The \emph{no-slip} boundary condition is used to model a viscous flow interacting with a moving wall. The velocity of the flow and the wall are the same which can be put in the mathematical form: + +$$ +\begin{equation} + \mathbf{u_{bc}} = \mathbf{u_{wall}} \ , +\end{equation} +$$ + +where $$\mathbf{u_{wall}}$$ is an user input parameter that is defaulted to the zero vector. Since this is a wall, the temperature is set to the wall temperature $$T_{wall}$$. The Lagrange pressure is set to its interior value: + +$$ +\begin{equation} +\left\{ +\begin{matrix} + \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + \mathbf{u}_{bc}(\mathbf{r}, t) &=& \mathbf{u_{wall}} \\ + T_{bc}(\mathbf{r}, t) &=& T_{wall} +\end{matrix} +\right. +\end{equation} +$$ + +All boundary gradients are set to the interior values: + +$$ +\begin{equation} + \partial_i f_{bc}(\mathbf{r}, t) = \partial_i f(\mathbf{r}, t) +\end{equation} +$$ + +In VERTEX-CFD the wall boundary velocity $$\mathbf{u_{wall}}$$ can linearly vary as a function of time. This is particularly useful when modeling flow with non-natural initial conditions (non-stationary flow over a stationary obstacle). The wall boundary velocity is set to initially match the flow velocity and linearly decreases to zero with time. This strategy is commonly adopted to ease the work of the numerical solver while developing steady-state flow. + + +###Rotating wall boundary condition +The \emph{rotating wall} boundary condition is used to model the interaction of a fluid with a rotating wall. The wall in contact with the fluid rotates with an angular velocity $$\omega_w$$ in the XY plane. The Lagrange pressure is set to the interior value, while the fluid velocity is computed from the angular velocity and the fluid temperature set to the wall temperature $$T_{wall}$$ as follows: + +$$ +\begin{equation} +\left\{ + \begin{matrix} + \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + \mathbf{u}_{bc}(\mathbf{r}, t) &=& \omega_{wall} \left(-y, x, 0.0 \right) \\ + T_{bc}(\mathbf{r}, t) &=& T_{wall} + \end{matrix} +\right. +\end{equation} +$$ + +The Cartesian coordinates are defined as $x$ and $y$ and the z-component of the velocity is assumed to be zero when used in three dimension. + +All boundary gradients are set to the interior values such as: + +$$ +\begin{equation} + \partial_i f_{bc}(\mathbf{r}, t) = \partial_i f(\mathbf{r}, t) +\end{equation} +$$ + ## Initial conditions From 4999dac4dd06d15312c32b88020465d32c395a01 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:04:44 -0500 Subject: [PATCH 151/214] upgrade --- docs/theory.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index d383adf..bbcc864 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -157,7 +157,7 @@ $$ \end{equation} $$ -The Cartesian coordinates are defined as $x$ and $y$ and the z-component of the velocity is assumed to be zero when used in three dimension. +The Cartesian coordinates are defined as $$x$$ and $$y$$ and the z-component of the velocity is assumed to be zero when used in three dimension. All boundary gradients are set to the interior values such as: @@ -167,5 +167,91 @@ $$ \end{equation} $$ +###Laminar flow +The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $u_i^{avg}$, the coordinates of the two points on the circle which can be connected by a line that pass through the circle center. The resultant inlet velocity profiles can be calculated as: + +$$ +\begin{equation} + u_i(x,y,z) = C_i u_i^{avg}\cdot\mathbf{n}\left(1.0 - \frac{r^2}{R^2}\right)~~, +\end{equation} +$$ + +where $$R$$ is the characteristic radius of the circle, $$\mathbf{r}$$ is the distance to the circle center which is calculated from the two points provided by the user, and $$C_i$$ is a constant based on the dimensionality of the problem. In 2D, $$C_i$$ is $3/2$, while in 3D, it is $2.0$. This boundary condition can be used with 2D rectangular channels and 3D pipes. The flow direction is calculated based on the surface normals, where the boundary condition is applied. An uniform inlet temperature can also be provided if solving for the temperature equation. The boundary conditions are as follows: + +$$ +\begin{equation} +\left\{ + \begin{matrix} + \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + \mathbf{u}_{bc}(\mathbf{r}, t) &=& \left(u_i\cdot n_x, u_i\cdot n_y, u_i\cdot n_z \right) \\ + T_{bc}(\mathbf{r}, t) &=& T_{inlet} + \end{matrix} +\right. +\end{equation} +$$ + +All boundary gradients are set to the interior values such as: + +$$ +\begin{equation} + \partial_i f_{bc}(\mathbf{r}, t) = \partial_i f(\mathbf{r}, t) +\end{equation} +$$ + +### Outflow boundary condition +The outflow boundary condition is employed when the back pressure is known at an outlet. The Lagrange pressure is computed from the back pressure $$P_b$$: + +$$ +\begin{equation} + \phi_{p,bc} = P_b +\end{equation} +$$ + +The velocity, the temperature and all boundary gradients are set to their respective interior values such as: + +$$ +\begin{equation} +\left\{ + \begin{matrix} + \mathbf{u}_{bc}(\mathbf{r}, t) &=& \mathbf{u}(\mathbf{r}, t) \\ + \partial_i U_{bc}(\mathbf{r}, t) &=& \partial_i U(\mathbf{r}, t) \\ + T_{bc}(\mathbf{r}, t) &=& T(\mathbf{r}, t) \\ + \partial_i T_{bc}(\mathbf{r}, t) &=& \partial_i T(\mathbf{r}, t) + \end{matrix} +\right. +\end{equation} +$$ + +### Cavity Lid + +A special boundary condition is implemented for the lid-driven cavity case to provide a smooth transition from the lid velocity to the no-slip condition at the wall, as described by Leriche and Gavrilakis \cite{Leriche2000}. The condition assumes that the domain consists of a two- or three-dimensional cube with half width $$h$$, centered on the origin. The Lagrange pressure at the boundary is set to the interior value: + +$$ +\begin{equation} + \phi_{bc} \left(\mathbf{r}, t \right) = \phi \left(\mathbf{r}, t \right) +\end{equation} +$$ + +If the energy equation is to be solved, the boundary temperature is set to a specified constant value: + +$$ +\begin{equation} + T_{bc} \left(\mathbf{r}, t \right)= T_b +\end{equation} +$$ + +The user is required to specify the index $$n$$ aligned with the boundary normal vector, and the index $v$ of the direction in which the velocity is aligned. The velocity components are then defined as: + +$$ +\begin{equation} + u_{i} \left(\mathbf{r}, t \right) = \left\{ \begin{matrix} + 0, i \neq v \\ + U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 + \end{matrix} + \right. +\end{equation} +$$ + +where $$U_0$$ is the nominal wall velocity, $$N$$ is the number of spatial directions, and $$r_i$$ is the distance of the current location from the origin in the $$i^{th}$$ direction. ## Initial conditions From 8b83cd4e12472bf4e9db8d2c018e9dbb25fd1e9f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:08:01 -0500 Subject: [PATCH 152/214] upgrade --- docs/theory.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/theory.md b/docs/theory.md index bbcc864..5ea8f33 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -255,3 +255,36 @@ $$ where $$U_0$$ is the nominal wall velocity, $$N$$ is the number of spatial directions, and $$r_i$$ is the distance of the current location from the origin in the $$i^{th}$$ direction. ## Initial conditions + +The initial conditions are specified for the velocity $$\mathbf{u}$$, the Lagrange pressure $$\phi_p$$ and the temperature $$T$$ when solving for the energy equation. The electric potential $$\varphi$$ is assumed constant and set to $$\varphi_0$$ in all initial conditions presented below. + +### Uniform initial conditions +The normalized Lagrange pressure, the velocity and the temperature are initialized to constant value across each block of the computational domain: + +$$ +\begin{align} + \left\{ + \begin{matrix} + \phi_p(\mathbf{r}, t) &=& \phi_{p,0} \\ + \mathbf{u}(\mathbf{r}, t) &=& \mathbf{u}_0 \\ + T(\mathbf{r}, t) &=& T_0 + \end{matrix} + \right. +\end{align} +$$ + +### Laminar flow +When using the laminar flow initial condition, the x-component of the velocity is initialized with a parabolic profile. The Lagrange pressure and the temperature are initialized to constant values. The current implementation can be used for two geometries that are a 2D rectangular channel and a 3D pipe. The parabolic profile is function of the average velocity and the channel height $$h$$ in 2D or the pipe diameter $$D$$ in 3D. It is assumed that the flow direction is aligned with the x-axis which yields the following expressions: + +$$ +\begin{align} + \left\{ + \begin{matrix} + \phi_p(\mathbf{r}, t) &=& \phi_{p,0} \\ + \mathbf{u}(\mathbf{r}, t) &=& \left(\frac{3}{2}\cdot u_{avg}\left(1.0 - \frac{y^2}{h}\right), 0.0 \right) \text{ in 2D} \\ + \mathbf{u}(\mathbf{r}, t) &=& \left(2\cdot u_{avg}\left(1.0 - \frac{y^2 + z^2}{D}\right), 0.0, 0.0 \right) \text{ in 3D} \\ + T(\mathbf{r}, t) &=& T_0 + \end{matrix} + \right. +\end{align} +$$ \ No newline at end of file From 06f94e3698bc2c22d981a39359502b84df298c5d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:10:38 -0500 Subject: [PATCH 153/214] upgrade --- docs/theory.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 5ea8f33..c752517 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -64,7 +64,7 @@ The boundary conditions implemented in VERTEX-CFD are listed below: - **Outflow with back pressure** - **Conducting and isolating wall** -The vector solution is denoted by $$U_{bc} = (\phi_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. +The vector solution is denoted by $$U_{bc} = (P_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. ### Dirichlet boundary The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $T_{bc}$. Linear ramping in time is also available and can be used to vary each primitive variable independently. @@ -74,7 +74,7 @@ $$ \left\{ \ \begin{matrix} u_{i,bc}(\mathbf{r}, t) = u_{i,D}(t) \\ - \phi_{p,{bc}}(\mathbf{r}, t) = \phi_p(\mathbf{r}, t) \\ + P_{p,{bc}}(\mathbf{r}, t) = P(\mathbf{r}, t) \\ \partial_i U_{bc}(\mathbf{r}, t) = \partial_i U(\mathbf{r}, t) \\ T_{bc}(\mathbf{r}, t) = T_{D} \end{matrix} @@ -89,7 +89,7 @@ $$ \begin{equation} \left\{ \begin{matrix} - \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + P_{bc}(\mathbf{r}, t) &=& P(\mathbf{r}, t) \\ \mathbf{u}_{bc}(\mathbf{r}, t) &=& \mathbf{u}(\mathbf{r}, t) - \left( \mathbf{u}(\mathbf{r}, t) \cdot \mathbf{n}_{bc} \right) \mathbf{n_{bc}} \\ T_{bc}(\mathbf{r}, t) &=& T(\mathbf{r}, t) \\ \varphi_{bc}(\mathbf{r}, t) &=& \varphi(\mathbf{r}, t) @@ -123,7 +123,7 @@ $$ \begin{equation} \left\{ \begin{matrix} - \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + P_{bc}(\mathbf{r}, t) &=& P(\mathbf{r}, t) \\ \mathbf{u}_{bc}(\mathbf{r}, t) &=& \mathbf{u_{wall}} \\ T_{bc}(\mathbf{r}, t) &=& T_{wall} \end{matrix} @@ -149,7 +149,7 @@ $$ \begin{equation} \left\{ \begin{matrix} - \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + P_{bc}(\mathbf{r}, t) &=& P(\mathbf{r}, t) \\ \mathbf{u}_{bc}(\mathbf{r}, t) &=& \omega_{wall} \left(-y, x, 0.0 \right) \\ T_{bc}(\mathbf{r}, t) &=& T_{wall} \end{matrix} @@ -182,7 +182,7 @@ $$ \begin{equation} \left\{ \begin{matrix} - \phi_{bc}(\mathbf{r}, t) &=& \phi(\mathbf{r}, t) \\ + P_{bc}(\mathbf{r}, t) &=& P(\mathbf{r}, t) \\ \mathbf{u}_{bc}(\mathbf{r}, t) &=& \left(u_i\cdot n_x, u_i\cdot n_y, u_i\cdot n_z \right) \\ T_{bc}(\mathbf{r}, t) &=& T_{inlet} \end{matrix} @@ -203,7 +203,7 @@ The outflow boundary condition is employed when the back pressure is known at an $$ \begin{equation} - \phi_{p,bc} = P_b + P_{p,bc} = P_b \end{equation} $$ @@ -228,7 +228,7 @@ A special boundary condition is implemented for the lid-driven cavity case to pr $$ \begin{equation} - \phi_{bc} \left(\mathbf{r}, t \right) = \phi \left(\mathbf{r}, t \right) + P_{bc} \left(\mathbf{r}, t \right) = P \left(\mathbf{r}, t \right) \end{equation} $$ @@ -256,7 +256,7 @@ where $$U_0$$ is the nominal wall velocity, $$N$$ is the number of spatial direc ## Initial conditions -The initial conditions are specified for the velocity $$\mathbf{u}$$, the Lagrange pressure $$\phi_p$$ and the temperature $$T$$ when solving for the energy equation. The electric potential $$\varphi$$ is assumed constant and set to $$\varphi_0$$ in all initial conditions presented below. +The initial conditions are specified for the velocity $$\mathbf{u}$$, the Lagrange pressure $$P$$ and the temperature $$T$$ when solving for the energy equation. The electric potential $$\varphi$$ is assumed constant and set to $$\varphi_0$$ in all initial conditions presented below. ### Uniform initial conditions The normalized Lagrange pressure, the velocity and the temperature are initialized to constant value across each block of the computational domain: @@ -265,7 +265,7 @@ $$ \begin{align} \left\{ \begin{matrix} - \phi_p(\mathbf{r}, t) &=& \phi_{p,0} \\ + P(\mathbf{r}, t) &=& P_{p,0} \\ \mathbf{u}(\mathbf{r}, t) &=& \mathbf{u}_0 \\ T(\mathbf{r}, t) &=& T_0 \end{matrix} @@ -280,7 +280,7 @@ $$ \begin{align} \left\{ \begin{matrix} - \phi_p(\mathbf{r}, t) &=& \phi_{p,0} \\ + P(\mathbf{r}, t) &=& P_{p,0} \\ \mathbf{u}(\mathbf{r}, t) &=& \left(\frac{3}{2}\cdot u_{avg}\left(1.0 - \frac{y^2}{h}\right), 0.0 \right) \text{ in 2D} \\ \mathbf{u}(\mathbf{r}, t) &=& \left(2\cdot u_{avg}\left(1.0 - \frac{y^2 + z^2}{D}\right), 0.0, 0.0 \right) \text{ in 3D} \\ T(\mathbf{r}, t) &=& T_0 From 20311273be3a18974e1eeb12123926a02968201c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:13:53 -0500 Subject: [PATCH 154/214] upgrade --- docs/theory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index c752517..c9c0f06 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -142,7 +142,7 @@ $$ In VERTEX-CFD the wall boundary velocity $$\mathbf{u_{wall}}$$ can linearly vary as a function of time. This is particularly useful when modeling flow with non-natural initial conditions (non-stationary flow over a stationary obstacle). The wall boundary velocity is set to initially match the flow velocity and linearly decreases to zero with time. This strategy is commonly adopted to ease the work of the numerical solver while developing steady-state flow. -###Rotating wall boundary condition +### Rotating wall boundary condition The \emph{rotating wall} boundary condition is used to model the interaction of a fluid with a rotating wall. The wall in contact with the fluid rotates with an angular velocity $$\omega_w$$ in the XY plane. The Lagrange pressure is set to the interior value, while the fluid velocity is computed from the angular velocity and the fluid temperature set to the wall temperature $$T_{wall}$$ as follows: $$ @@ -167,7 +167,7 @@ $$ \end{equation} $$ -###Laminar flow +### Laminar flow The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $u_i^{avg}$, the coordinates of the two points on the circle which can be connected by a line that pass through the circle center. The resultant inlet velocity profiles can be calculated as: $$ From e5a7fecd93bb0a1dfda76d8c996a1aee51e67f65 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:18:57 -0500 Subject: [PATCH 155/214] upgrade --- docs/theory.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index c9c0f06..0b2c7ba 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -48,6 +48,34 @@ $$ VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. +A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:incomp-ns}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $U_h$ of $U$ can be expressed as + +$$ +\begin{equation} + U_h = \sum_i U_i \phi_i~~, +\end{equation} +$$ + +where $$\phi_i \in V^p_h$$ is a basis function. A weak form of Eq. \ref{eq:incomp-ns} is obtained by substituting $$U$$ with $$U_h$$, taking an inner product between each term of the partial differential equation and a test function and integrating over the computational volume: + +$$ +\begin{equation}\label{eq:weak-form} + \iiint \left[ \phi^T \partial_t U + \phi^T \nabla \cdot F(U) - \phi^T \nabla \cdot G(U, \nabla U) - \phi^T S(U) \right] d\Omega = 0.0~~. +\end{equation} +$$ + +Equation \ref{eq:weak-form} can be further transformed by integrating per part the conservative fluxes to yield boundary fluxes denoted by the under script $$bc$$: + +$$ +\begin{align}\label{eq:weak-form-bc} + \oiiint \phi^T \partial_t U d\Omega - \oiiint \nabla \phi^T \cdot F(U) d\Omega + \\ \oiiint \nabla \cdot \phi^T \cdot G(U, \nabla U) d\Omega - \oiiint \phi^T S(U) d\Omega \nonumber = \\ -\oiint \phi^T F_{bc}(U) \Vec{n} d \delta \Omega + \oiint \phi^T G_{bc}(U, \nabla U) \Vec{n} d \delta \Omega~~. +\end{align} +$$ + +Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $G$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section \ref{sec:bcs}. + +The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $\phi_i$ of order $p$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. + ## Boundary conditions @@ -240,7 +268,7 @@ $$ \end{equation} $$ -The user is required to specify the index $$n$$ aligned with the boundary normal vector, and the index $v$ of the direction in which the velocity is aligned. The velocity components are then defined as: +The user is required to specify the index $$n$$ aligned with the boundary normal vector, and the index $$v$$ of the direction in which the velocity is aligned. The velocity components are then defined as: $$ \begin{equation} From 7fcef972d5ee1c7276a3b6bb76fd1eb647e4916e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:22:42 -0500 Subject: [PATCH 156/214] upgrade --- docs/theory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 0b2c7ba..4e909ba 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -68,13 +68,13 @@ Equation \ref{eq:weak-form} can be further transformed by integrating per part t $$ \begin{align}\label{eq:weak-form-bc} - \oiiint \phi^T \partial_t U d\Omega - \oiiint \nabla \phi^T \cdot F(U) d\Omega + \\ \oiiint \nabla \cdot \phi^T \cdot G(U, \nabla U) d\Omega - \oiiint \phi^T S(U) d\Omega \nonumber = \\ -\oiint \phi^T F_{bc}(U) \Vec{n} d \delta \Omega + \oiint \phi^T G_{bc}(U, \nabla U) \Vec{n} d \delta \Omega~~. +\iiint \phi^T \partial_t U d\Omega - \iiint \nabla \phi^T \cdot F(U) d\Omega + \\ \oiiint \nabla \cdot \phi^T \cdot G(U, \nabla U) d\Omega - \oiiint \phi^T S(U) d\Omega \nonumber = \\ -\iint \phi^T F_{bc}(U) \Vec{n} d \delta \Omega + \iint \phi^T G_{bc}(U, \nabla U) \Vec{n} d \delta \Omega~~. \end{align} $$ -Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $G$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section \ref{sec:bcs}. +Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section \ref{sec:bcs}. -The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $\phi_i$ of order $p$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. +The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. ## Boundary conditions From da819dcf844111ea06aedd962b5cd032f594a2f7 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:24:03 -0500 Subject: [PATCH 157/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 4e909ba..0ed06a5 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -68,7 +68,7 @@ Equation \ref{eq:weak-form} can be further transformed by integrating per part t $$ \begin{align}\label{eq:weak-form-bc} -\iiint \phi^T \partial_t U d\Omega - \iiint \nabla \phi^T \cdot F(U) d\Omega + \\ \oiiint \nabla \cdot \phi^T \cdot G(U, \nabla U) d\Omega - \oiiint \phi^T S(U) d\Omega \nonumber = \\ -\iint \phi^T F_{bc}(U) \Vec{n} d \delta \Omega + \iint \phi^T G_{bc}(U, \nabla U) \Vec{n} d \delta \Omega~~. +\iiint \phi^T \partial_t U d\Omega - \iiint \nabla \phi^T \cdot F(U) d\Omega + \\ \iiint \nabla \cdot \phi^T \cdot G(U, \nabla U) d\Omega - \iiint \phi^T S(U) d\Omega \nonumber = \\ -\iint \phi^T F_{bc}(U) \vec{n} d \delta \Omega + \iint \phi^T G_{bc}(U, \nabla U) \vec{n} d \delta \Omega~~. \end{align} $$ From 305c7e36021b85c4649f361d89e732cc7ee0e3f1 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:26:22 -0500 Subject: [PATCH 158/214] upgrade --- docs/theory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 0ed06a5..f0daca8 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -30,7 +30,7 @@ $$ The equations are recast in a conservative form and solved for the pressure $$P$$, the velocity $$\mathbf{u}$$, the temperature $$T$$, and the electric potential $$\varphi$$. $$ -\begin{align} +\begin{align}\label{eq:pdes} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -48,7 +48,7 @@ $$ VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. -A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:incomp-ns}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $U_h$ of $U$ can be expressed as +A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:pdes}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $U_h$ of $U$ can be expressed as $$ \begin{equation} From 148cfab8a52274a2ca7553940a8fdac9c290d635 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:28:42 -0500 Subject: [PATCH 159/214] upgrade --- docs/theory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index f0daca8..797ad56 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,7 +11,7 @@ usemathjax: true ## Governing equations -VERTEX-CFD implements the incompressible Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). Coupling between the different equations is ensured by the Buoyancy force and the Lorentz force. +VERTEX-CFD implements the entropically damped artificial compressibility (EDAC) Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). Coupling between the different equations is ensured by the Buoyancy force and the Lorentz force. $$ \begin{align} @@ -44,11 +44,11 @@ $$ $$ -## The discretized equations +## Discretized equations VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. -A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:pdes}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $U_h$ of $U$ can be expressed as +A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:pdes}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $$U_h$$ of $$U$$ can be expressed as $$ \begin{equation} From 07f301be2793f7a6b796e28e8b6eb7394f05e55f Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:38:01 -0500 Subject: [PATCH 160/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 797ad56..265f44f 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -77,7 +77,7 @@ Boundary fluxes are evaluated at quadrature points, and their contribution is ad The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. -## Boundary conditions +## Boundary conditions\label{sec:bcs} Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. The boundary conditions implemented in VERTEX-CFD are listed below: From 660df343d4369c50e86cadc9a661044bc7658893 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:39:31 -0500 Subject: [PATCH 161/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 265f44f..52c9d2b 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -56,7 +56,7 @@ $$ \end{equation} $$ -where $$\phi_i \in V^p_h$$ is a basis function. A weak form of Eq. \ref{eq:incomp-ns} is obtained by substituting $$U$$ with $$U_h$$, taking an inner product between each term of the partial differential equation and a test function and integrating over the computational volume: +where $$\phi_i \in V^p_h$$ is a basis function. A weak form of Eq. \ref{eq:pdes} is obtained by substituting $$U$$ with $$U_h$$, taking an inner product between each term of the partial differential equation and a test function and integrating over the computational volume: $$ \begin{equation}\label{eq:weak-form} From a86c610456fb8bb5f478a6254daf6cba2ea21cd8 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:43:49 -0500 Subject: [PATCH 162/214] upgrade --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 52c9d2b..96da737 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -72,7 +72,7 @@ $$ \end{align} $$ -Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section \ref{sec:bcs}. +Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section [Boundary conditions](#boundary-conditions). The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. From d52592f3eb648b85f10a65ffb427f6ddb31f0612 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 3 Feb 2025 22:49:46 -0500 Subject: [PATCH 163/214] upgrade --- docs/theory.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 96da737..e342694 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -77,20 +77,20 @@ Boundary fluxes are evaluated at quadrature points, and their contribution is ad The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. -## Boundary conditions\label{sec:bcs} +## Boundary conditions Boundary conditions are weakly imposed by computing numerical flux at the boundaries' provided boundary values. The boundary conditions implemented in VERTEX-CFD are listed below: The boundary conditions implemented in VERTEX-CFD are listed below: -- **Periodic boundary** -- **Dirichlet with time-transient variation** -- **Symmetry for isothermal flow** -- **No-slip for viscous flow** -- **Rotating wall for isothermal flow** -- **Laminar flow** -- **Outflow with back pressure** -- **Conducting and isolating wall** +- Periodic boundary +- [Dirichlet with time-transient variation](#dirichlet-boundary) +- [Symmetry for isothermal flow](#symmetry-boundary-condition) +- [No-slip for viscous flow](#no-slip-boundary-condition) +- [Rotating wall for isothermal flow](#rotating-wall-boundary-condition) +- [Laminar flow](#laminar-flow) +- [Outflow with back pressure](#outflow-boundary-condition) +- [Cavity Lid](#cavity-Lid) The vector solution is denoted by $$U_{bc} = (P_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. From 4e03fa097168a780c5b413af1c9c741a5cc7de90 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Thu, 13 Feb 2025 07:08:48 -0500 Subject: [PATCH 164/214] update --- index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index 96c73f1..3acdbc7 100644 --- a/index.md +++ b/index.md @@ -23,9 +23,9 @@ We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how - [Kellis Kincaid](https://www.ornl.gov/staff-profile/kellis-c-kincaid) - [Furkan Oz](https://www.ornl.gov/staff-profile/furkan-oz) - [Kalyan Gottiparthi](https://www.ornl.gov/staff-profile/kalyan-c-gottiparthi) -- [Jason Degraw](https://www.ornl.gov/staff-profile/jason-w-degraw) +- [Jason W. DeGraw](https://www.ornl.gov/staff-profile/jason-w-degraw) - [Doug Stefanski](https://www.ornl.gov/staff-profile/douglas-l-stefanski) -- [Filipe Leite Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) +- [Filipe L. Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) ## Citing If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](DOI_NUMBER) of the version you used as a software citation: From d39c46fddaf0393142ef5f474dc468d833120289 Mon Sep 17 00:00:00 2001 From: fo7 Date: Mon, 17 Feb 2025 14:22:31 -0500 Subject: [PATCH 165/214] Installation instructions are updated with the spack-trilinos installation. --- docs/installation.md | 229 ++++++++++++++++++++++++++++++------------- docs/usage.md | 6 +- 2 files changed, 164 insertions(+), 71 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 46d1825..59879b0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,110 +5,203 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, please refer to GPU installation. Otherwise, check CPU installation below. +VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, Trilinos needs to be compiled with CUDA module included. For the details, refer to the GPU installation section. + +## CPU Installation +VERTEX-CFD is installed on top of Trilinos package. Hence it requires Trilinos installed on the system. If Trilinos is already installed, you can skip to VERTEX-CFD installation. Otherwise, Trilinos installation instructions are given below. + +### Trilinos Insllation +Before Trilinos installation, we suggest using a compute node instead of login node as this will be a demanding process. Depending on the CPU configurations, with 32 cores, expect around 4 hours of building/installation time. For the installation, create a install directory and get Spack. -## CPU installation -For the CPU installation, first of all, make sure you have `TRILINOS_ROOT` variable defined in your environment. -``` -TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION -``` -Once the `TRILINOS_ROOT` is set, load the dependencies: ``` -module load intel -module load tbb -module load compiler-rt/ -module load mkl -module load python +mkdir trilinos +cd trilinos/ +git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git ``` -Once the environment is ready, configuration file can be run as: + +Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for Spack. Pay special attention to `EDIT` marks locations where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section. + ``` -./vertexcfd-env +spack: + concretizer: + unify: when_possible + + config: + build_stage: + - $spack/var/spack/spack-stage/build-$arch-$date/ + misc_cache: $spack/.cache + build_jobs: 16 # EDIT: change the number with the number of available cores. + install_tree: + root: $spack/opt/spack/ + projections: + all: install-{os}-{target}-{compiler.name}-{compiler.version}/{name}-{version} + keep_stage: true + verify_ssl: true + checksum: true + dirty: false + build_language: C + ccache: false + db_lock_timeout: 120 + package_lock_timeout: null + shared_linking: rpath + allow_sgid: true + locks: true + suppress_gpg_warnings: false + connect_timeout: 10 + + compilers: + - compiler: # EDIT: change below with your compiler of choice + spec: gcc@13.2.0 + paths: + cc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gcc + cxx: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/g++ + f77: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran + fc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran + flags: {} + operating_system: centos7 # EDIT: update according to `spack arch`: just the OS version + target: x86_64 + modules: # EDIT: update with a list of modules you want to load by default + - jobutils/1.0 + - StdEnv + - python/3.11.5 + - openmpi/4.1.0 + + packages: + all: + target: [broadwell] # EDIT: Change this according to `spack arch` + compiler: [gcc@13.2.0] # EDIT: Change according to your compiler of choice + providers: # EDIT: Change versions below according to the default in your machine + mpi: [openmpi] + blas: [intel-oneapi-mkl] + lapack: [intel-oneapi-mkl] + pkgconfig: [pkg-config] + variants: +mpi + + specs: + - ninja + - boost + - intel-oneapi-mkl + - googletest + - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo ``` -The content of the `vertexcfd-env` file is: + +Now, source the Spack setup script and create an environment named vertex. + ``` -#!/bin/sh -SOURCE=PATH_TO/vertex-cfd -INSTALL=INSTALLATION_PATH -TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION -BUILD="Release" -BUILD_SYSTEM=Ninja +source spack/share/spack/setup-env.sh +spack env create vertex spack-trilinos16.yaml +spack env activate vertex +spack spec +``` -rm -rf CMake* -rm DartConfiguration.tcl -rm CTestTestfile.cmake -rm VertexCFDConfig.cmake -rm -rf Testing + `spack spec` will show the configuration of what is to be installed, make sure the configuration matches what was requested in the configuration file. Next, install Trilinos and its dependencies. -cmake \ - -G "$BUILD_SYSTEM" \ - -D CMAKE_BUILD_TYPE="$BUILD" \ - -D CMAKE_INSTALL_PREFIX="$INSTALL" \ - -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ - -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ - -D VertexCFD_ENABLE_TESTING=ON \ - -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ - -D TRILINOS_ROOT="$TRILINOS_ROOT" \ - \ - ${SOURCE} ``` -Please note that `BUILD_SYSTEM` environment can be both `Ninja` or `Make`. Choose the correct one based on your system. Once the configuration is completed, compilation can be initiated with following command. Please note that we used `ninja` in this example so please switch to `make` if your `BUILD_SYSTEM` is defined as `Make`. +spack install +``` + +If the session is ended for any reason, the user might need to reactivate the vertex spack env using `spack env activate vertex`. If all compiles without error, the modules are ready to use. `spack location -i trilinos` will return the location of the Trilinos install. The modules can be setup using + ``` -ninja +spack module tcl refresh ``` -By default, `ninja` command will use the available cores, this can be limitted as: +Trilinos can be called by loading the module as shown below. The folder names may be different depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on CADES in ORNL, they are located in: + ``` -ninja -j4 +module use //share/spack/modules/linux-centos7-haswell/ +module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -You can replace the number 4 with the number of cores that you prefer. We do recommend to use `-j` flag as some systems tends to compile on every CPUs on the login node and fail due to the allocated/used CPUs. Once the compilation is done, VERTEX-CFD can be installed by using: + +### VERTEX-CFD Installation +Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. As a reminder for users skipped the Trilinos installation, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the CPU installation, first of all, create an environment script with a `vertex-env.sh` name to load all the modules required for the installation. The example configuration script is given below. Depending on your machine configuration, some of the folders may change, such as `linux-centos7-haswell` might be `linux-centos7-broadwell` in your system. ``` -ninja install +#!/bin/bash + +install=//share/spack/modules/linux-centos7-haswell/ +echo $install + +module load gcc/13.2.0 + +module use $install +module load gcc-runtime +module load openmpi +module load cmake +module load ninja +module load boost +module load netcdf-c +module load parmetis +module load intel-oneapi-mkl +module load cgns +module load googletest +module load hdf5 +module load zlib-ng +module load trilinos/16.0.0-gcc-13.2.0-qzebvtj +HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ +ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ + +GCC_ROOT=// +unset LIBRARY_PATH ``` -Once installed, VERTEX-CFD is ready to run. -## GPU installation -For the GPU installation, CUDA needs to be loaded in the HPC environment and Trilinos needs to be built with the GPU support. Just like the CPU version, define the `TRILINOS_ROOT` variable as: +Once you created `vertex-env.sh` script, source it as follows: ``` -TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA +source vertex-env.sh ``` -Once the `TRILINOS_ROOT` is set, load the dependencies by including cuda: +Now, you can create a folder for VERTEX-CFD and clone from GitHub by using: ``` -module load intel -module load tbb -module load compiler-rt/ -module load mkl -module load python -module load cuda +mkdir VERTEX-CFD-CPU +cd VERTEX-CFD-CPU +git clone https://github.com/ORNL/VERTEX-CFD.git ``` -Once the environment is ready, configuration file can be run as: +This will clone the VERTEX-CFD folder. In order to build it, create a new folder as build as follows: ``` -./vertexcfd-env-gpu +mkdir build +cd build ``` -The content of the `vertexcfd-env-gpu` file is: +In order to configure VERTEX-CFD, create a configuration script with `vertex-configuration.sh` name, which contains the following lines: ``` -#!/bin/sh +#!/bin/sh -SOURCE=PATH_TO/vertex-cfd -INSTALL=INSTALLATION_PATH -TRILINOS_ROOT=PATH_TO_TRILINOS/TRILINOS_VERSION_WITH_CUDA -BUILD="Release" -BUILD_SYSTEM=Ninja +SOURCE=/ +INSTALL="" +BUILD="RelWithDebInfo" rm -rf CMake* +rm -rf .ninja* rm DartConfiguration.tcl rm CTestTestfile.cmake +rm build.ninja rm VertexCFDConfig.cmake rm -rf Testing +# Unset variable set by spack modules. +# If this is present, any directories present will be treated +# as "implicit" library paths by CMake and it will strip them +# out of the executable RPATHS. Then you have to set +# LD_LIBRARY_PATH appropriately to run jobs. +unset LIBRARY_PATH + cmake \ - -G "$BUILD_SYSTEM" \ - -D CMAKE_BUILD_TYPE="$BUILD" \ - -D CMAKE_INSTALL_PREFIX="$INSTALL" \ + -D CMAKE_BUILD_TYPE=${BUILD} \ + -D CMAKE_INSTALL_PREFIX=${INSTALL} \ -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ -D VertexCFD_ENABLE_TESTING=ON \ - -D CLANG_FORMAT_EXECUTABLE="PATH_TO_CLANG_FORMAT" \ - -D TRILINOS_ROOT="$TRILINOS_ROOT" \ + -D Trilinos_ROOT= \ \ ${SOURCE} ``` +Once the configuration script is ready, run it as follows: +``` +./vertex-configuration.sh +``` + +When configured succesfully, you can use make to install as: +``` +make -j install +``` +If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. + +## GPU Installation +GPU instructions are under development... diff --git a/docs/usage.md b/docs/usage.md index 6c82246..7a82429 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -7,13 +7,13 @@ usemathjax: true # Usage -Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, the executable is located in `INSTALL_PATH/bin/vertexcfd`. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. +Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, the executable is located in `/bin/vertexcfd`. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. ## Running a simulation In order to run a simulation in serial, vertexcfd can be called directly as: ``` -INSTALL_PATH/bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +/bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml ``` To run in parallel, `mpirun` is required. An example script for the SLURM scheduler is below: ``` @@ -29,7 +29,7 @@ source PATH_TO_ENVIRONMENT_SCRIPT export OMP_PROC_BIND=true export OMP_PLACES=threads -mpirun INSTALL_PATH/bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml +mpirun /bin/vertexcfd --i=/incompressible_2d_channel.xml ``` Once the simulation starts, the example output should look like: ``` From ec315133058ac71f4f94f6992e6f186cc2277988 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 17 Feb 2025 21:31:28 -0500 Subject: [PATCH 166/214] update --- docs/contribution.md | 8 ++++++++ docs/installation.md | 2 +- index.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 docs/contribution.md diff --git a/docs/contribution.md b/docs/contribution.md new file mode 100644 index 0000000..937424c --- /dev/null +++ b/docs/contribution.md @@ -0,0 +1,8 @@ +--- +parent: VERTEX-CFD v1.0 User Guide +title: Contributing to VERTEX-CFD +nav_order: 4 +usemathjax: true +--- + +# How to contribute to VERTEX-CFD diff --git a/docs/installation.md b/docs/installation.md index 59879b0..ff86790 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -10,7 +10,7 @@ VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, ## CPU Installation VERTEX-CFD is installed on top of Trilinos package. Hence it requires Trilinos installed on the system. If Trilinos is already installed, you can skip to VERTEX-CFD installation. Otherwise, Trilinos installation instructions are given below. -### Trilinos Insllation +### Trilinos Installation Before Trilinos installation, we suggest using a compute node instead of login node as this will be a demanding process. Depending on the CPU configurations, with 32 cores, expect around 4 hours of building/installation time. For the installation, create a install directory and get Spack. ``` diff --git a/index.md b/index.md index 3acdbc7..23c10f4 100644 --- a/index.md +++ b/index.md @@ -16,7 +16,7 @@ An open-source CFD code for multi-physics modeling and simulation with a focus o --- ## Contributing -We encourage you to contribute to VERTEX-CFD! Please check the guidelines on how to do so. +We encourage you to contribute to VERTEX-CFD! Please review the [how to contribute](docs/contribution.md) page. #### Contributors - [Marco Delchini](https://www.ornl.gov/staff-profile/marc-olivier-delchini) From 12c213b7a1553a2c8f53383a19b4b500df19fcdf Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 17 Feb 2025 21:44:10 -0500 Subject: [PATCH 167/214] update --- docs/contribution.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/contribution.md b/docs/contribution.md index 937424c..74dbb7d 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -6,3 +6,16 @@ usemathjax: true --- # How to contribute to VERTEX-CFD + +Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed in the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD please follow the following steps: +- Git clone VERTEX-CFD repo and compile the source code. +- Create a new branch from the main branch. +- Make changes to the source code. +- Add/update unit test(s) and/or regression test(s) when needed. +- Push changes to your remote branch and create a pull request (PR). +- Add a description to the pull request with results supporting the changes. +- Tag a reviewer to the PR. + +A contributor will then review the PR and add comments to the changes. Make sure to address all comments and to respond to all threads. Once the reviewer approves the PR, the branch will be merged to the main branch. + +If you have any questions or comment, please contact of the main contributors listed in the home page. \ No newline at end of file From e12e657436fbec50326f78c931d45b05f3da13ab Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 17 Feb 2025 21:46:01 -0500 Subject: [PATCH 168/214] update --- docs/contribution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contribution.md b/docs/contribution.md index 74dbb7d..ab30c8f 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -18,4 +18,4 @@ Contribution to VERTEX-CFD source code follows a strict process to warranty robu A contributor will then review the PR and add comments to the changes. Make sure to address all comments and to respond to all threads. Once the reviewer approves the PR, the branch will be merged to the main branch. -If you have any questions or comment, please contact of the main contributors listed in the home page. \ No newline at end of file +If you have any questions or comment, please contact of the main contributors listed in the [home page](../index.md). \ No newline at end of file From f33d89f634cba782e3b50cd3550b58981f735e0e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 17 Feb 2025 22:06:06 -0500 Subject: [PATCH 169/214] update --- docs/contribution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contribution.md b/docs/contribution.md index ab30c8f..1c8a51e 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -8,7 +8,7 @@ usemathjax: true # How to contribute to VERTEX-CFD Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed in the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD please follow the following steps: -- Git clone VERTEX-CFD repo and compile the source code. +- [Git clone VERTEX-CFD repo](https://github.com/ORNL/VERTEX-CFD) and [compile the source code](installation.md). - Create a new branch from the main branch. - Make changes to the source code. - Add/update unit test(s) and/or regression test(s) when needed. From 66b858fe0ab319ad312955822cb5799fd15de48a Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 18 Feb 2025 14:20:52 -0500 Subject: [PATCH 170/214] GPU installation instructions are added. --- docs/installation.md | 223 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 4 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index ff86790..8957790 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,9 +8,9 @@ nav_order: 1 VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, Trilinos needs to be compiled with CUDA module included. For the details, refer to the GPU installation section. ## CPU Installation -VERTEX-CFD is installed on top of Trilinos package. Hence it requires Trilinos installed on the system. If Trilinos is already installed, you can skip to VERTEX-CFD installation. Otherwise, Trilinos installation instructions are given below. +VERTEX-CFD is installed on top of Trilinos package. Hence it requires Trilinos installed on the system. If Trilinos-CPU is already installed, you can skip to VERTEX-CFD-CPU installation. Otherwise, Trilinos-CPU installation instructions are given below. -### Trilinos Installation +### Trilinos-CPU Installation Before Trilinos installation, we suggest using a compute node instead of login node as this will be a demanding process. Depending on the CPU configurations, with 32 cores, expect around 4 hours of building/installation time. For the installation, create a install directory and get Spack. ``` @@ -113,7 +113,7 @@ module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -### VERTEX-CFD Installation +### VERTEX-CFD-CPU Installation Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. As a reminder for users skipped the Trilinos installation, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the CPU installation, first of all, create an environment script with a `vertex-env.sh` name to load all the modules required for the installation. The example configuration script is given below. Depending on your machine configuration, some of the folders may change, such as `linux-centos7-haswell` might be `linux-centos7-broadwell` in your system. ``` #!/bin/bash @@ -204,4 +204,219 @@ make -j install If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. ## GPU Installation -GPU instructions are under development... +VERTEX-CFD-GPU version requires Trilinos-16-0-0 build with GPU support. If you have it, you can skip to VERTEX-CFD-GPU Installation section. Otherwise, you need to build Trilinos-16-0-0 with GPU support. + +### Trilinos-GPU Installation +VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. Similar to CPU installation, GPU installation takes a long time as well. We suggest building Trilinos on a compute node rather than login node. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: + +``` +git clone https://github.com/trilinos/Trilinos.git +cd Trilinos +git checkout trilinos-release-16-0-branch +``` + +Once you checked out to the correct version, you can create a new folder for the building Trilinos as follows: + +``` +mkdir build +cd build +``` + +After that, modules required to build Trilinos needs to be loaded. For that, create a file with `trilinos-cuda-env.sh` name. Although the procedure for the module loading changes depend on the machine, as an example, the content of `trilinos-cuda-env.sh` in NERSC Perlmutter is as follows and you can adjust it according to your machine configurations: + +``` +module load PrgEnv-gnu/8.5.0 +module load gcc-native/12.3 +module load cudatoolkit/12.4 +module load craype-accel-nvidia80 +module load cray-libsci/23.12.5 +module load craype/2.7.30 +module load cray-mpich/8.1.28 +module load cray-hdf5-parallel/1.12.2.9 +module load cray-netcdf-hdf5parallel/4.9.0.9 +module load cray-parallel-netcdf/1.12.3.9 +module load cmake/3.30.2 + +module load spack +spack env activate cuda +spack load metis +spack load parmetis + +export MPICH_ENV_DISPLAY=1 +export MPICH_VERSION_DISPLAY=1 +export OMP_STACKSIZE=128M +export OMP_PROC_BIND=spread +export OMP_PLACES=threads +export HDF5_USE_FILE_LOCKING=FALSE +export MPICH_GPU_SUPPORT_ENABLED=1 +export CUDATOOLKIT_VERSION_STRING=${CRAY_CUDATOOLKIT_VERSION#*_} + +export CRAY_ACCEL_TARGET="nvidia80" + +export KOKKOS_MAP_DEVICE_ID_BY=mpi_rank +export CUDA_MANAGED_FORCE_DEVICE_ALLOC=1 +export TPETRA_ASSUME_GPU_AWARE_MPI=0 +``` + +Once this is ready, create a configuration script with `trilinos-cuda-config.sh` name and the following content: + +``` +#!/bin/bash +# This is a sample Trilinos configuration script for Albany on perlmutter + +source trilinos-cuda-env.sh + +# Cleanup old cmake files +rm -rf CMake* + +# Set Trilinos build path +BUILD_DIR=`pwd` + +# EDIT: Change this based on your NVCC WRAPPER location +NVCC_WRAPPER= + +# FIXME: update with path to Trilinos source directory +TRILINOS_SOURCE_DIR= + +# FIXME: point to boost install or use boost from project directory +BOOST_DIR= + +TRILINOS_INSTALL_DIR= + +export CRAYPE_LINK_TYPE=dynamic +export CRAY_CPU_TARGET=x86-64 + +cmake \ + -D CMAKE_C_COMPILER=cc \ + -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ + -D CMAKE_Fortran_COMPILER=ftn \ +\ + -D CMAKE_INSTALL_PREFIX:PATH=${TRILINOS_INSTALL_DIR} \ + -D CMAKE_BUILD_TYPE:STRING=RELEASE \ +\ + -D BUILD_SHARED_LIBS=ON \ + -D Trilinos_ENABLE_ALL_PACKAGES=ON \ + -D Trilinos_ENABLE_SECONDARY_TESTED_CODE=ON \ + -D Trilinos_ENABLE_EXPLICIT_INSTANTIATION=ON \ + -D Trilinos_ENABLE_PyTrilinos=OFF \ + -D Trilinos_SHOW_DEPRECATED_WARNINGS=OFF \ + -D TPL_ENABLE_MPI=ON \ + -D TPL_Netcdf_PARALLEL=FALSE \ +\ + -D TPL_ENABLE_HDF5:STRING=ON \ + -D HDF5_INCLUDE_DIRS:PATH=${HDF5_DIR}/include \ + -D HDF5_LIBRARY_DIRS:PATH=${HDF5_DIR}/lib\ +\ + -D TPL_ENABLE_Netcdf=ON \ + -D Netcdf_INCLUDE_DIRS:PATH=${NETCDF_DIR}/include \ + -D Netcdf_LIBRARY_DIRS:PATH=${NETCDF_DIR}/lib \ +\ + -D Kokkos_ENABLE_CUDA:BOOL=ON \ + -D Kokkos_ENABLE_CUDA_LAMBDA:BOOL=ON \ + -D Kokkos_ENABLE_CUDA_UVM:BOOL=OFF \ + -D Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC:BOOL=OFF \ +\ + -D TPL_ENABLE_Boost:BOOL=ON \ + -D TPL_ENABLE_BoostLib=ON \ + -D Boost_INCLUDE_DIRS:PATH=${BOOST_DIR}/include \ + -D Boost_LIBRARY_DIRS:PATH=${BOOST_DIR}/lib \ +\ + -D TPL_ENABLE_Krino=OFF \ + -D TPL_ENABLE_Matio=OFF \ + -D TPL_ENABLE_CGNS=OFF \ + -D TPL_ENABLE_METIS=ON \ + -D TPL_ENABLE_ParMETIS=ON \ + -D Trilinos_ENABLE_Stokhos:BOOL=OFF \ +\ + -D TPL_ENABLE_SuperLU:BOOL=OFF \ + -D ML_ENABLE_SuperLU:BOOL=OFF \ + -D TPL_ENABLE_BLAS:BOOL=ON \ + -D TPL_BLAS_LIBRARIES:STRING="${CRAY_PE_LIBSCI_PREFIX_DIR}/lib/libsci_gnu.so" \ + -D TPL_ENABLE_LAPACK:BOOL=ON \ + -D TPL_LAPACK_LIBRARIES:STRING="${CRAY_PE_LIBSCI_PREFIX_DIR}/lib/libsci_gnu.so" \ +\ +${TRILINOS_SOURCE_DIR} +``` + +After that, run the `trilinos-cuda-config.sh` script as: + +``` +./trilinos-cuda-config.sh +``` + +Once the configuration is completed, you can build and install Trilinos-16-0-0-GPU as follows: + +``` +make -j install +``` + +After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD-GPU installation. + +### VERTEX-CFD-GPU Installation +Once the Trilinos-GPU is installed in your system and ready to use, VERTEX-CFD-GPU can be installed. As a reminder for users skipped the Trilinos and VERTEX-CFD-CPU installations, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the GPU installation, first of all, create an environment script with a `vertex-env-gpu.sh` name to load all the modules required for the installation. + +``` +``` +Once you created `vertex-env-gpu.sh` script, source it as follows: +``` +source vertex-env-gpu.sh +``` +Now, you can create a folder for VERTEX-CFD-GPU and clone from GitHub by using: +``` +mkdir VERTEX-CFD-GPU +cd VERTEX-CFD-GPU +git clone https://github.com/ORNL/VERTEX-CFD.git +``` +This will clone the VERTEX-CFD folder. In order to build it, create a new folder as build as follows: +``` +mkdir build +cd build +``` +In order to configure VERTEX-CFD, create a configuration script with `vertex-configuration-gpu.sh` name, which contains the following lines: +``` +#!/bin/sh + +# Set directories for source code and install files +SRC=/ +INSTALL= + +# Select build type +BUILD="RelWithDebInfo" + +TRILINOS_INSTALL_DIR= + +# Clean old files +rm -rf CMake* + +NVCC_WRAPPER= + +# Unset variable set by spack modules. +# If this is present, any directories present will be treated +# as "implicit" library paths by CMake and it will strip them +# out of the executable RPATHS. Then you have to set +# LD_LIBRARY_PATH appropriately to run jobs. +# unset LIBRARY_PATH + +# Run CMake +cmake \ + -G Ninja \ + -D Trilinos_DIR:FILEPATH=${TRILINOS_INSTALL_DIR} \ + -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ + -D CMAKE_BUILD_TYPE="$BUILD" \ + -D CMAKE_INSTALL_PREFIX="$INSTALL" \ + -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color -Wno-cpp -Wno-deprecated" \ + -D VertexCFD_ENABLE_TESTING=OFF \ + \ + ${SRC} +``` +Once the configuration script is ready, run it as follows: +``` +./vertex-configuration-gpu.sh +``` + +When configured succesfully, you can use make to install as: +``` +make -j install +``` +If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. + From 62ce95a894acf10d7d6bc39fc2dd8bc3fded1828 Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 18 Feb 2025 14:24:48 -0500 Subject: [PATCH 171/214] Small fix on a comment --- docs/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 8957790..aeafb11 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -275,10 +275,10 @@ BUILD_DIR=`pwd` # EDIT: Change this based on your NVCC WRAPPER location NVCC_WRAPPER= -# FIXME: update with path to Trilinos source directory +# EDIT: Change with path to Trilinos source directory TRILINOS_SOURCE_DIR= -# FIXME: point to boost install or use boost from project directory +# EDIT: Change with path to boost BOOST_DIR= TRILINOS_INSTALL_DIR= From c7e7ac1d56226b7d112a65a4380b9aafb85d1beb Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 18 Feb 2025 14:42:25 -0500 Subject: [PATCH 172/214] Update to the configuration description. --- docs/installation.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index aeafb11..8d8b45b 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -338,7 +338,7 @@ cmake \ ${TRILINOS_SOURCE_DIR} ``` -After that, run the `trilinos-cuda-config.sh` script as: +As an example, in NERSC Perlmutter system, for `NVCC_WRAPPER`, we used the wrapper available in Sandia National Lab's GitHub page `https://github.com/sandialabs/Albany/blob/master/doc/LandIce/machines/perlmutter/nvcc_wrapper_a100`. After that, run the `trilinos-cuda-config.sh` script as: ``` ./trilinos-cuda-config.sh @@ -353,13 +353,10 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD-GPU installation. ### VERTEX-CFD-GPU Installation -Once the Trilinos-GPU is installed in your system and ready to use, VERTEX-CFD-GPU can be installed. As a reminder for users skipped the Trilinos and VERTEX-CFD-CPU installations, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the GPU installation, first of all, create an environment script with a `vertex-env-gpu.sh` name to load all the modules required for the installation. +Once the Trilinos-GPU is installed in your system and ready to use, VERTEX-CFD-GPU can be installed. As a reminder for users skipped the Trilinos and VERTEX-CFD-CPU installations, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the GPU installation, you will need `trilinos-cuda-env.sh` script one more time. If due to any reason, you had to close the terminal, you need to source it one more time. Otherwise, you can skip to next step without sourcing it. ``` -``` -Once you created `vertex-env-gpu.sh` script, source it as follows: -``` -source vertex-env-gpu.sh +source /trilinos-cuda-env.sh ``` Now, you can create a folder for VERTEX-CFD-GPU and clone from GitHub by using: ``` @@ -409,7 +406,8 @@ cmake \ \ ${SRC} ``` -Once the configuration script is ready, run it as follows: +For those skipped Trilinos installation script, the NVCC_WRAPPER that is used in NERSC Perlmutter system is available in Sandia National Lab's GitHub page `https://github.com/sandialabs/Albany/blob/master/doc/LandIce/machines/perlmutter/nvcc_wrapper_a100`. Once the configuration script is ready, run it as follows: + ``` ./vertex-configuration-gpu.sh ``` From 7af1e6023880e45835636ca9230c0d8c491589b3 Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 18 Feb 2025 22:20:25 -0500 Subject: [PATCH 173/214] Installation instructions are updated and relevant scripts are added. --- docs/installation.md | 330 +++++++++++------- .../slurm-submission-regression-test.sh | 20 ++ docs/scripts/slurm-submission-run.sh | 13 + docs/scripts/slurm-submission-unit-test.sh | 19 + docs/scripts/spack-trilinos16.yaml | 61 ++++ docs/scripts/trilinos-cuda-config.sh | 75 ++++ docs/scripts/trilinos-cuda-env.sh | 33 ++ docs/scripts/vertex-config.sh | 34 ++ docs/scripts/vertex-env-cpu.sh | 26 ++ 9 files changed, 478 insertions(+), 133 deletions(-) create mode 100644 docs/scripts/slurm-submission-regression-test.sh create mode 100644 docs/scripts/slurm-submission-run.sh create mode 100644 docs/scripts/slurm-submission-unit-test.sh create mode 100644 docs/scripts/spack-trilinos16.yaml create mode 100644 docs/scripts/trilinos-cuda-config.sh create mode 100644 docs/scripts/trilinos-cuda-env.sh create mode 100644 docs/scripts/vertex-config.sh create mode 100644 docs/scripts/vertex-env-cpu.sh diff --git a/docs/installation.md b/docs/installation.md index 8d8b45b..50da39c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,13 +5,13 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. For full CPU and GPU capabilities, Trilinos needs to be compiled with CUDA module included. For the details, refer to the GPU installation section. +VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to VERTEX-CFD installation instructions. All of the scripts given in this file is also available in `docs/scripts` directory for users convenience. -## CPU Installation -VERTEX-CFD is installed on top of Trilinos package. Hence it requires Trilinos installed on the system. If Trilinos-CPU is already installed, you can skip to VERTEX-CFD-CPU installation. Otherwise, Trilinos-CPU installation instructions are given below. +## Trilinos Installation +Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have spack based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. ### Trilinos-CPU Installation -Before Trilinos installation, we suggest using a compute node instead of login node as this will be a demanding process. Depending on the CPU configurations, with 32 cores, expect around 4 hours of building/installation time. For the installation, create a install directory and get Spack. +First of all, create a directory and get Spack. ``` mkdir trilinos @@ -88,7 +88,6 @@ spack: Now, source the Spack setup script and create an environment named vertex. ``` - source spack/share/spack/setup-env.sh spack env create vertex spack-trilinos16.yaml spack env activate vertex @@ -113,101 +112,8 @@ module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -### VERTEX-CFD-CPU Installation -Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. As a reminder for users skipped the Trilinos installation, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the CPU installation, first of all, create an environment script with a `vertex-env.sh` name to load all the modules required for the installation. The example configuration script is given below. Depending on your machine configuration, some of the folders may change, such as `linux-centos7-haswell` might be `linux-centos7-broadwell` in your system. -``` -#!/bin/bash - -install=//share/spack/modules/linux-centos7-haswell/ -echo $install - -module load gcc/13.2.0 - -module use $install -module load gcc-runtime -module load openmpi -module load cmake -module load ninja -module load boost -module load netcdf-c -module load parmetis -module load intel-oneapi-mkl -module load cgns -module load googletest -module load hdf5 -module load zlib-ng -module load trilinos/16.0.0-gcc-13.2.0-qzebvtj -HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ -ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ - -GCC_ROOT=// -unset LIBRARY_PATH -``` - -Once you created `vertex-env.sh` script, source it as follows: -``` -source vertex-env.sh -``` -Now, you can create a folder for VERTEX-CFD and clone from GitHub by using: -``` -mkdir VERTEX-CFD-CPU -cd VERTEX-CFD-CPU -git clone https://github.com/ORNL/VERTEX-CFD.git -``` -This will clone the VERTEX-CFD folder. In order to build it, create a new folder as build as follows: -``` -mkdir build -cd build -``` -In order to configure VERTEX-CFD, create a configuration script with `vertex-configuration.sh` name, which contains the following lines: -``` -#!/bin/sh - -SOURCE=/ -INSTALL="" -BUILD="RelWithDebInfo" - -rm -rf CMake* -rm -rf .ninja* -rm DartConfiguration.tcl -rm CTestTestfile.cmake -rm build.ninja -rm VertexCFDConfig.cmake -rm -rf Testing - -# Unset variable set by spack modules. -# If this is present, any directories present will be treated -# as "implicit" library paths by CMake and it will strip them -# out of the executable RPATHS. Then you have to set -# LD_LIBRARY_PATH appropriately to run jobs. -unset LIBRARY_PATH - -cmake \ - -D CMAKE_BUILD_TYPE=${BUILD} \ - -D CMAKE_INSTALL_PREFIX=${INSTALL} \ - -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ - -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ - -D VertexCFD_ENABLE_TESTING=ON \ - -D Trilinos_ROOT= \ - \ - ${SOURCE} -``` -Once the configuration script is ready, run it as follows: -``` -./vertex-configuration.sh -``` - -When configured succesfully, you can use make to install as: -``` -make -j install -``` -If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. - -## GPU Installation -VERTEX-CFD-GPU version requires Trilinos-16-0-0 build with GPU support. If you have it, you can skip to VERTEX-CFD-GPU Installation section. Otherwise, you need to build Trilinos-16-0-0 with GPU support. - ### Trilinos-GPU Installation -VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. Similar to CPU installation, GPU installation takes a long time as well. We suggest building Trilinos on a compute node rather than login node. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: +VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: ``` git clone https://github.com/trilinos/Trilinos.git @@ -225,6 +131,8 @@ cd build After that, modules required to build Trilinos needs to be loaded. For that, create a file with `trilinos-cuda-env.sh` name. Although the procedure for the module loading changes depend on the machine, as an example, the content of `trilinos-cuda-env.sh` in NERSC Perlmutter is as follows and you can adjust it according to your machine configurations: ``` +#!/bin/bash + module load PrgEnv-gnu/8.5.0 module load gcc-native/12.3 module load cudatoolkit/12.4 @@ -350,18 +258,13 @@ Once the configuration is completed, you can build and install Trilinos-16-0-0-G make -j install ``` -After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD-GPU installation. +After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. -### VERTEX-CFD-GPU Installation -Once the Trilinos-GPU is installed in your system and ready to use, VERTEX-CFD-GPU can be installed. As a reminder for users skipped the Trilinos and VERTEX-CFD-CPU installations, we suggest using a compute node instead of login node for the installation. Otherwise, building has a chance to fail. For the GPU installation, you will need `trilinos-cuda-env.sh` script one more time. If due to any reason, you had to close the terminal, you need to source it one more time. Otherwise, you can skip to next step without sourcing it. - -``` -source /trilinos-cuda-env.sh +## VERTEX Installation +Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: ``` -Now, you can create a folder for VERTEX-CFD-GPU and clone from GitHub by using: -``` -mkdir VERTEX-CFD-GPU -cd VERTEX-CFD-GPU +mkdir VERTEX-CFD +cd VERTEX-CFD git clone https://github.com/ORNL/VERTEX-CFD.git ``` This will clone the VERTEX-CFD folder. In order to build it, create a new folder as build as follows: @@ -369,52 +272,213 @@ This will clone the VERTEX-CFD folder. In order to build it, create a new folder mkdir build cd build ``` -In order to configure VERTEX-CFD, create a configuration script with `vertex-configuration-gpu.sh` name, which contains the following lines: + +In the build directory, depending on the CPU/GPU installation, you will need different environment files. For GPU, you can use `trilinos-cuda-config.sh`. There is no need for a separate configuration script. However, for CPU, you will need `vertex-env-cpu.sh`. Those environment scripts are system specific depending and it may change based on your system. Carefully modify them based on your system. The content of the `vertex-env-cpu.sh` is as follows: ``` -#!/bin/sh +#!/bin/bash + +install=//share/spack/modules/linux-centos7-haswell/ +echo $install -# Set directories for source code and install files -SRC=/ -INSTALL= +module load gcc/13.2.0 -# Select build type -BUILD="RelWithDebInfo" +module use $install +module load gcc-runtime +module load openmpi +module load cmake +module load ninja +module load boost +module load netcdf-c +module load parmetis +module load intel-oneapi-mkl +module load cgns +module load googletest +module load hdf5 +module load zlib-ng +module load trilinos/16.0.0-gcc-13.2.0-qzebvtj +HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ +ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ + +GCC_ROOT=// +unset LIBRARY_PATH +``` + +Please note that, those scripts are system specific. Hence you will need to modify them depending on your system. Please carefully modify them and make sure they are loading the correct modules. The scripts that we shared are succesfully used on CADES (CPU configuration) in ORNL and NERSC Perlmutter (GPU configuration). Once you have the environment script, source the script with one of the lines below based on CPU/GPU installation: + +``` +source vertex-env-cpu.sh +source trilinos-cuda-env.sh +``` + +Once the environment script is sourced, VERTEX-CFD needs to be configured. Create a configuration script with `vertex-config.sh` name, which contains the following lines: +``` +#!/bin/sh -TRILINOS_INSTALL_DIR= +SOURCE=/ +INSTALL="" +BUILD="RelWithDebInfo" -# Clean old files rm -rf CMake* +rm -rf .ninja* +rm DartConfiguration.tcl +rm CTestTestfile.cmake +rm build.ninja +rm VertexCFDConfig.cmake +rm -rf Testing -NVCC_WRAPPER= +# EDIT: Uncomment this line for GPU and update this based on your NVCC WRAPPER location +#NVCC_WRAPPER= # Unset variable set by spack modules. # If this is present, any directories present will be treated # as "implicit" library paths by CMake and it will strip them # out of the executable RPATHS. Then you have to set # LD_LIBRARY_PATH appropriately to run jobs. -# unset LIBRARY_PATH +unset LIBRARY_PATH -# Run CMake cmake \ - -G Ninja \ - -D Trilinos_DIR:FILEPATH=${TRILINOS_INSTALL_DIR} \ - -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ - -D CMAKE_BUILD_TYPE="$BUILD" \ - -D CMAKE_INSTALL_PREFIX="$INSTALL" \ - -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color -Wno-cpp -Wno-deprecated" \ - -D VertexCFD_ENABLE_TESTING=OFF \ + -D CMAKE_BUILD_TYPE=${BUILD} \ + -D CMAKE_INSTALL_PREFIX=${INSTALL} \ +# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ + -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ + -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ + -D VertexCFD_ENABLE_TESTING=ON \ + -D Trilinos_ROOT= \ \ - ${SRC} + ${SOURCE} ``` -For those skipped Trilinos installation script, the NVCC_WRAPPER that is used in NERSC Perlmutter system is available in Sandia National Lab's GitHub page `https://github.com/sandialabs/Albany/blob/master/doc/LandIce/machines/perlmutter/nvcc_wrapper_a100`. Once the configuration script is ready, run it as follows: - +Please notice that you need to define `NVCC_WRAPPER` and `CMAKE_CXX_COMPILER` for GPU version. Once the configuration script is ready, run it as follows: ``` -./vertex-configuration-gpu.sh +./vertex-config.sh ``` When configured succesfully, you can use make to install as: ``` make -j install ``` -If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. +If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. Once the installation is completed, unit and regression tests can be run to make sure everything is working as expected. For the instructions for the testing, refer to the following sections. + +# Testing +VERTEX-CFD development procedure strictly requires to develop a unit/regression test for each capability added. Hence, it is suggested to run those tests to make sure installation is properly completed and the functionalities are working as expected. + +## Unit Tests +VERTEX-CFD has large amount of unit tests. In order to run those, you can use the below job submission scripts written for SLURM scheduler. + +``` +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node 32 +#SBATCH --time 0:10:00 +#SBATCH --output output.log +#SBATCH --error error.log + +source /trilinos-cuda-config.sh +#source /vertex-env-cpu.sh # For GPU comment above line and use this line. + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +export IOSS_PROPERTIES="COMPOSE_RESULTS=on:MINIMIZE_OPEN_FILES=on:MAXIMUM_NAME_LENGTH=64:DUPLICATE_FIELD_NAME_BEHAVIOR=WARNING" +export EXODUS_NETCDF4=1 + +export MAP_STRING=slot:pe=${SLURM_CPUS_PER_TASK} + +ctest -j ${SLURM_NTASKS_PER_NODE} +``` + +For other scheduler, such as PBS, you can convert the SLURM submission script. The example output screen for the unit tests is: +``` + Start 1: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_1 + Start 2: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_2 + Start 3: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_4 + Start 5: VertexCFD_KokkosMPI_test_OPENMP_np_2_nt_1 + Start 6: VertexCFD_KokkosMPI_test_OPENMP_np_2_nt_2 + Start 7: VertexCFD_KokkosMPI_test_OPENMP_np_2_nt_4 + Start 8: VertexCFD_KokkosMPI_test_OPENMP_np_4_nt_1 + 1/554 Test #8: VertexCFD_KokkosMPI_test_OPENMP_np_4_nt_1 .......................................... Passed 2.03 sec + Start 12: VertexCFD_EvaluatorTestHarness_test_OPENMP_np_1_nt_1 + Start 13: VertexCFD_EvaluatorTestHarness_test_OPENMP_np_1_nt_2 + Start 23: VertexCFD_Version_test_OPENMP_nt_1 + 2/554 Test #2: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_2 .......................................... Passed 2.61 sec + Start 16: VertexCFD_EvaluatorTestHarness_test_OPENMP_np_2_nt_1 + 3/554 Test #1: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_1 .......................................... Passed 2.61 sec + Start 27: VertexCFD_ParameterPack_test_OPENMP_nt_1 + 4/554 Test #7: VertexCFD_KokkosMPI_test_OPENMP_np_2_nt_4 .......................................... Passed 2.61 sec + Start 9: VertexCFD_KokkosMPI_test_OPENMP_np_4_nt_2 + 5/554 Test #3: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_4 .......................................... Passed 2.62 sec + . + . + . + 553/554 Test #550: VertexCFD_DivergenceAdvectionTest_test_OPENMP_nt_192 ............................... Passed 0.84 sec + Start 554: VertexCFD_FullInductionMHDProperties_test_OPENMP_nt_192 + 554/554 Test #554: VertexCFD_FullInductionMHDProperties_test_OPENMP_nt_192 ............................ Passed 0.36 sec + + 97% tests passed, 14 tests failed out of 554 + + Total Test time (real) = 1825.19 sec + + The following tests FAILED: + 11 - VertexCFD_KokkosMPI_test_OPENMP_np_192_nt_1 (Failed) + 22 - VertexCFD_EvaluatorTestHarness_test_OPENMP_np_192_nt_1 (Failed) + 71 - VertexCFD_MeshManager_test_OPENMP_nt_1 (Failed) + 72 - VertexCFD_MeshManager_test_OPENMP_nt_2 (Failed) + 73 - VertexCFD_MeshManager_test_OPENMP_nt_4 (Failed) + 74 - VertexCFD_MeshManager_test_OPENMP_nt_192 (Failed) + 79 - VertexCFD_InitialConditionManager_test_OPENMP_nt_1 (Failed) + 80 - VertexCFD_InitialConditionManager_test_OPENMP_nt_2 (Failed) + 81 - VertexCFD_InitialConditionManager_test_OPENMP_nt_4 (Failed) + 82 - VertexCFD_InitialConditionManager_test_OPENMP_nt_192 (Failed) + 169 - VertexCFD_ExternalFields_test_OPENMP_np_192_nt_1 (Failed) + 180 - VertexCFD_Restart_test_OPENMP_np_192_nt_1 (Failed) + 191 - VertexCFD_GeometryPrimitives_test_OPENMP_np_192_nt_1 (Failed) + 202 - VertexCFD_WriteMatrix_test_OPENMP_np_192_nt_1 (Failed) +``` +Please note that there are several tests that are used on the continious integration (CI) and they fail on any other system. This is the reason of the failed tests and it is expected to fail on those tests. + +## Regression Tests +VERTEX-CFD uses regression tests to make sure the new capabilities are not introducing bugs/errors to the old capabilities. In order to run regression tests, you can use the below submission script written for SLURM scheduler: + +``` +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node 32 +#SBATCH --output output.log +#SBATCH --error error.log +#SBATCH --job-name=regression-test + +source /trilinos-cuda-config.sh +#source /vertex-env-cpu.sh # For GPU comment above line and use this line. + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +export IOSS_PROPERTIES="COMPOSE_RESULTS=on:MINIMIZE_OPEN_FILES=on:MAXIMUM_NAME_LENGTH=64:DUPLICATE_FIELD_NAME_BEHAVIOR=WARNING" +export EXODUS_NETCDF4=1 + +export MAP_STRING=slot:pe=${SLURM_CPUS_PER_TASK} + +cd regression_test +pytest -k "test" +``` + +The example regression test output is: + +``` +============================= test session starts ============================== +platform linux -- Python 3.9.16, pytest-6.2.5, py-1.11.0, pluggy-1.5.0 +rootdir: /regression_test, configfile: pytest.ini +collected 37 items + +test_full_induction_mhd/test_current_sheet/test_current_sheet.py::TestCurrentSheet::test_2d_long PASSED [ 6%] +test_full_induction_mhd/test_mhd_ldc/test_mhd_ldc.py::TestMHDLDC::test_2d_rotated_mhd_ldc[bx] PASSED [ 13%] +test_full_induction_mhd/test_mhd_ldc/test_mhd_ldc.py::TestMHDLDC::test_2d_rotated_mhd_ldc[by] PASSED [ 20%] +. +. +. +test_incompressible/test_taylor_green_vortex/test_taylor_green_vortex.py::TestTaylorGreenVortex::test_2d_mesh_convergence_laminar PASSED [ 93%] +test_incompressible/test_wale_cavity/test_wale_cavity.py::TestWaleCavity::test_3d_wale_cavity PASSED [100%] +========== 0 failed, 37 passed, 0 deselected in 40525.94s (11:15:25) ========== +``` + + diff --git a/docs/scripts/slurm-submission-regression-test.sh b/docs/scripts/slurm-submission-regression-test.sh new file mode 100644 index 0000000..943017f --- /dev/null +++ b/docs/scripts/slurm-submission-regression-test.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node 32 +#SBATCH --output output.log +#SBATCH --error error.log +#SBATCH --job-name=regression-test + +source /trilinos-cuda-config.sh +#source /vertex-env-cpu.sh # For GPU comment above line and use this line. + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +export IOSS_PROPERTIES="COMPOSE_RESULTS=on:MINIMIZE_OPEN_FILES=on:MAXIMUM_NAME_LENGTH=64:DUPLICATE_FIELD_NAME_BEHAVIOR=WARNING" +export EXODUS_NETCDF4=1 + +export MAP_STRING=slot:pe=${SLURM_CPUS_PER_TASK} + +cd regression_test +pytest -k "test" diff --git a/docs/scripts/slurm-submission-run.sh b/docs/scripts/slurm-submission-run.sh new file mode 100644 index 0000000..575ce47 --- /dev/null +++ b/docs/scripts/slurm-submission-run.sh @@ -0,0 +1,13 @@ +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node=32 +#SBATCH --time=1:00:00 +#SBATCH -o output.log +#SBATCH -e error.log + +source PATH_TO_ENVIRONMENT_SCRIPT + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +mpirun /bin/vertexcfd --i=/incompressible_2d_channel.xml diff --git a/docs/scripts/slurm-submission-unit-test.sh b/docs/scripts/slurm-submission-unit-test.sh new file mode 100644 index 0000000..142bee1 --- /dev/null +++ b/docs/scripts/slurm-submission-unit-test.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#SBATCH -N 1 +#SBATCH --ntasks-per-node 32 +#SBATCH --time 0:10:00 +#SBATCH --output output.log +#SBATCH --error error.log + +source /trilinos-cuda-config.sh +#source /vertex-env-cpu.sh # For GPU comment above line and use this line. + +export OMP_PROC_BIND=true +export OMP_PLACES=threads + +export IOSS_PROPERTIES="COMPOSE_RESULTS=on:MINIMIZE_OPEN_FILES=on:MAXIMUM_NAME_LENGTH=64:DUPLICATE_FIELD_NAME_BEHAVIOR=WARNING" +export EXODUS_NETCDF4=1 + +export MAP_STRING=slot:pe=${SLURM_CPUS_PER_TASK} + +ctest -j ${SLURM_NTASKS_PER_NODE} diff --git a/docs/scripts/spack-trilinos16.yaml b/docs/scripts/spack-trilinos16.yaml new file mode 100644 index 0000000..7330f93 --- /dev/null +++ b/docs/scripts/spack-trilinos16.yaml @@ -0,0 +1,61 @@ +spack: + concretizer: + unify: when_possible + + config: + build_stage: + - $spack/var/spack/spack-stage/build-$arch-$date/ + misc_cache: $spack/.cache + build_jobs: 16 # EDIT: change the number with the number of available cores. + install_tree: + root: $spack/opt/spack/ + projections: + all: install-{os}-{target}-{compiler.name}-{compiler.version}/{name}-{version} + keep_stage: true + verify_ssl: true + checksum: true + dirty: false + build_language: C + ccache: false + db_lock_timeout: 120 + package_lock_timeout: null + shared_linking: rpath + allow_sgid: true + locks: true + suppress_gpg_warnings: false + connect_timeout: 10 + + compilers: + - compiler: # EDIT: change below with your compiler of choice + spec: gcc@13.2.0 + paths: + cc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gcc + cxx: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/g++ + f77: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran + fc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran + flags: {} + operating_system: centos7 # EDIT: update according to `spack arch`: just the OS version + target: x86_64 + modules: # EDIT: update with a list of modules you want to load by default + - jobutils/1.0 + - StdEnv + - python/3.11.5 + - openmpi/4.1.0 + + packages: + all: + target: [broadwell] # EDIT: Change this according to `spack arch` + compiler: [gcc@13.2.0] # EDIT: Change according to your compiler of choice + providers: # EDIT: Change versions below according to the default in your machine + mpi: [openmpi] + blas: [intel-oneapi-mkl] + lapack: [intel-oneapi-mkl] + pkgconfig: [pkg-config] + variants: +mpi + + specs: + - ninja + - boost + - intel-oneapi-mkl + - googletest + - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo diff --git a/docs/scripts/trilinos-cuda-config.sh b/docs/scripts/trilinos-cuda-config.sh new file mode 100644 index 0000000..2465a87 --- /dev/null +++ b/docs/scripts/trilinos-cuda-config.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# This is a sample Trilinos configuration script for Albany on perlmutter + +source trilinos-cuda-env.sh + +# Cleanup old cmake files +rm -rf CMake* + +# Set Trilinos build path +BUILD_DIR=`pwd` + +# EDIT: Change this based on your NVCC WRAPPER location +NVCC_WRAPPER= + +# EDIT: Change with path to Trilinos source directory +TRILINOS_SOURCE_DIR= + +# EDIT: Change with path to boost +BOOST_DIR= + +TRILINOS_INSTALL_DIR= + +export CRAYPE_LINK_TYPE=dynamic +export CRAY_CPU_TARGET=x86-64 + +cmake \ + -D CMAKE_C_COMPILER=cc \ + -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ + -D CMAKE_Fortran_COMPILER=ftn \ +\ + -D CMAKE_INSTALL_PREFIX:PATH=${TRILINOS_INSTALL_DIR} \ + -D CMAKE_BUILD_TYPE:STRING=RELEASE \ +\ + -D BUILD_SHARED_LIBS=ON \ + -D Trilinos_ENABLE_ALL_PACKAGES=ON \ + -D Trilinos_ENABLE_SECONDARY_TESTED_CODE=ON \ + -D Trilinos_ENABLE_EXPLICIT_INSTANTIATION=ON \ + -D Trilinos_ENABLE_PyTrilinos=OFF \ + -D Trilinos_SHOW_DEPRECATED_WARNINGS=OFF \ + -D TPL_ENABLE_MPI=ON \ + -D TPL_Netcdf_PARALLEL=FALSE \ +\ + -D TPL_ENABLE_HDF5:STRING=ON \ + -D HDF5_INCLUDE_DIRS:PATH=${HDF5_DIR}/include \ + -D HDF5_LIBRARY_DIRS:PATH=${HDF5_DIR}/lib\ +\ + -D TPL_ENABLE_Netcdf=ON \ + -D Netcdf_INCLUDE_DIRS:PATH=${NETCDF_DIR}/include \ + -D Netcdf_LIBRARY_DIRS:PATH=${NETCDF_DIR}/lib \ +\ + -D Kokkos_ENABLE_CUDA:BOOL=ON \ + -D Kokkos_ENABLE_CUDA_LAMBDA:BOOL=ON \ + -D Kokkos_ENABLE_CUDA_UVM:BOOL=OFF \ + -D Kokkos_ENABLE_IMPL_CUDA_MALLOC_ASYNC:BOOL=OFF \ +\ + -D TPL_ENABLE_Boost:BOOL=ON \ + -D TPL_ENABLE_BoostLib=ON \ + -D Boost_INCLUDE_DIRS:PATH=${BOOST_DIR}/include \ + -D Boost_LIBRARY_DIRS:PATH=${BOOST_DIR}/lib \ +\ + -D TPL_ENABLE_Krino=OFF \ + -D TPL_ENABLE_Matio=OFF \ + -D TPL_ENABLE_CGNS=OFF \ + -D TPL_ENABLE_METIS=ON \ + -D TPL_ENABLE_ParMETIS=ON \ + -D Trilinos_ENABLE_Stokhos:BOOL=OFF \ +\ + -D TPL_ENABLE_SuperLU:BOOL=OFF \ + -D ML_ENABLE_SuperLU:BOOL=OFF \ + -D TPL_ENABLE_BLAS:BOOL=ON \ + -D TPL_BLAS_LIBRARIES:STRING="${CRAY_PE_LIBSCI_PREFIX_DIR}/lib/libsci_gnu.so" \ + -D TPL_ENABLE_LAPACK:BOOL=ON \ + -D TPL_LAPACK_LIBRARIES:STRING="${CRAY_PE_LIBSCI_PREFIX_DIR}/lib/libsci_gnu.so" \ +\ +${TRILINOS_SOURCE_DIR} diff --git a/docs/scripts/trilinos-cuda-env.sh b/docs/scripts/trilinos-cuda-env.sh new file mode 100644 index 0000000..f9afb96 --- /dev/null +++ b/docs/scripts/trilinos-cuda-env.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +module load PrgEnv-gnu/8.5.0 +module load gcc-native/12.3 +module load cudatoolkit/12.4 +module load craype-accel-nvidia80 +module load cray-libsci/23.12.5 +module load craype/2.7.30 +module load cray-mpich/8.1.28 +module load cray-hdf5-parallel/1.12.2.9 +module load cray-netcdf-hdf5parallel/4.9.0.9 +module load cray-parallel-netcdf/1.12.3.9 +module load cmake/3.30.2 + +module load spack +spack env activate cuda +spack load metis +spack load parmetis + +export MPICH_ENV_DISPLAY=1 +export MPICH_VERSION_DISPLAY=1 +export OMP_STACKSIZE=128M +export OMP_PROC_BIND=spread +export OMP_PLACES=threads +export HDF5_USE_FILE_LOCKING=FALSE +export MPICH_GPU_SUPPORT_ENABLED=1 +export CUDATOOLKIT_VERSION_STRING=${CRAY_CUDATOOLKIT_VERSION#*_} + +export CRAY_ACCEL_TARGET="nvidia80" + +export KOKKOS_MAP_DEVICE_ID_BY=mpi_rank +export CUDA_MANAGED_FORCE_DEVICE_ALLOC=1 +export TPETRA_ASSUME_GPU_AWARE_MPI=0 diff --git a/docs/scripts/vertex-config.sh b/docs/scripts/vertex-config.sh new file mode 100644 index 0000000..bf263cd --- /dev/null +++ b/docs/scripts/vertex-config.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +SOURCE=/ +INSTALL="" +BUILD="RelWithDebInfo" + +rm -rf CMake* +rm -rf .ninja* +rm DartConfiguration.tcl +rm CTestTestfile.cmake +rm build.ninja +rm VertexCFDConfig.cmake +rm -rf Testing + +# EDIT: Uncomment this line for GPU and update this based on your NVCC WRAPPER location +#NVCC_WRAPPER= + +# Unset variable set by spack modules. +# If this is present, any directories present will be treated +# as "implicit" library paths by CMake and it will strip them +# out of the executable RPATHS. Then you have to set +# LD_LIBRARY_PATH appropriately to run jobs. +unset LIBRARY_PATH + +cmake \ + -D CMAKE_BUILD_TYPE=${BUILD} \ + -D CMAKE_INSTALL_PREFIX=${INSTALL} \ +# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ + -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ + -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ + -D VertexCFD_ENABLE_TESTING=ON \ + -D Trilinos_ROOT= \ + \ + ${SOURCE} diff --git a/docs/scripts/vertex-env-cpu.sh b/docs/scripts/vertex-env-cpu.sh new file mode 100644 index 0000000..4cca807 --- /dev/null +++ b/docs/scripts/vertex-env-cpu.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +install=//share/spack/modules/linux-centos7-haswell/ +echo $install + +module load gcc/13.2.0 + +module use $install +module load gcc-runtime +module load openmpi +module load cmake +module load ninja +module load boost +module load netcdf-c +module load parmetis +module load intel-oneapi-mkl +module load cgns +module load googletest +module load hdf5 +module load zlib-ng +module load trilinos/16.0.0-gcc-13.2.0-qzebvtj +HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ +ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ + +GCC_ROOT=// +unset LIBRARY_PATH From 802b15d666c14341ec7657be77adf2477e043474 Mon Sep 17 00:00:00 2001 From: fo7 Date: Tue, 18 Feb 2025 22:22:10 -0500 Subject: [PATCH 174/214] typo is fixed. --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 50da39c..453ddf0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -260,7 +260,7 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. -## VERTEX Installation +## VERTEX-CFD Installation Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: ``` mkdir VERTEX-CFD From a424cef18d250d6b7d4986d9c65fe6444bc8bf28 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:35:53 -0500 Subject: [PATCH 175/214] update --- docs/installation.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 453ddf0..afccfdc 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -11,7 +11,7 @@ VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have spack based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. ### Trilinos-CPU Installation -First of all, create a directory and get Spack. +First of all, create a directory and git clone Spack. ``` mkdir trilinos @@ -19,7 +19,7 @@ cd trilinos/ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git ``` -Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for Spack. Pay special attention to `EDIT` marks locations where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section. +Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for Spack. Pay special attention to `EDIT` marks locations where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section at the end of the file. ``` spack: @@ -105,12 +105,13 @@ If the session is ended for any reason, the user might need to reactivate the ve ``` spack module tcl refresh ``` -Trilinos can be called by loading the module as shown below. The folder names may be different depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on CADES in ORNL, they are located in: +Trilinos can be called by loading the module as shown below. The folder names may be different depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on [CADES](https://www.ornl.gov/content/cades) in ORNL, they are located in: ``` module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` +Trilinos and its dependencies are required to compile [VERTEX-CFD](#markdown-vertex-cfd-installation). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: @@ -128,7 +129,7 @@ mkdir build cd build ``` -After that, modules required to build Trilinos needs to be loaded. For that, create a file with `trilinos-cuda-env.sh` name. Although the procedure for the module loading changes depend on the machine, as an example, the content of `trilinos-cuda-env.sh` in NERSC Perlmutter is as follows and you can adjust it according to your machine configurations: +After that, modules required to build Trilinos need to be loaded. To this purpose, create a file with `trilinos-cuda-env.sh` name. Although the procedure for the module loading changes depend on the machine, as an example, the content of `trilinos-cuda-env.sh` in NERSC Perlmutter is as follows and you can adjust it according to your machine configurations: ``` #!/bin/bash @@ -260,7 +261,7 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. -## VERTEX-CFD Installation +##VERTEX-CFD Installation Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: ``` mkdir VERTEX-CFD From 5cc532d20e3f570ea3d86d767d22b1ac198e866e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:38:04 -0500 Subject: [PATCH 176/214] update --- docs/installation.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index afccfdc..c71804e 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,6 +1,6 @@ --- parent: VERTEX-CFD v1.0 User Guide -title: Installation +title: Installation and testing nav_order: 1 --- @@ -261,7 +261,8 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. -##VERTEX-CFD Installation +## VERTEX-CFD Installation + Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: ``` mkdir VERTEX-CFD From 554f477c1faea8248894854c415b3012afe38fec Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:45:13 -0500 Subject: [PATCH 177/214] update --- docs/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index c71804e..acdea2c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -435,10 +435,10 @@ For other scheduler, such as PBS, you can convert the SLURM submission script. T 191 - VertexCFD_GeometryPrimitives_test_OPENMP_np_192_nt_1 (Failed) 202 - VertexCFD_WriteMatrix_test_OPENMP_np_192_nt_1 (Failed) ``` -Please note that there are several tests that are used on the continious integration (CI) and they fail on any other system. This is the reason of the failed tests and it is expected to fail on those tests. +Note: several unit tests rely on restart files that are not compatible across architecture. The above list of tests is expected to fail outside of the continuous integration pipelines. ## Regression Tests -VERTEX-CFD uses regression tests to make sure the new capabilities are not introducing bugs/errors to the old capabilities. In order to run regression tests, you can use the below submission script written for SLURM scheduler: +VERTEX-CFD uses regression tests to make sure the new capabilities does not introduce bugs/errors to the source code. In order to run regression tests, you can use the below submission script written for SLURM scheduler: ``` #!/bin/bash From 7137fdf91531832f5b69cba78573f5c56e941896 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:49:02 -0500 Subject: [PATCH 178/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index acdea2c..3394d87 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -111,7 +111,7 @@ Trilinos can be called by loading the module as shown below. The folder names ma module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -Trilinos and its dependencies are required to compile [VERTEX-CFD](#markdown-vertex-cfd-installation). +Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertex-cfd-installation). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: From c333afcb2b4a4d8417c2e2785a739d9aac679180 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:53:53 -0500 Subject: [PATCH 179/214] update --- docs/installation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 3394d87..625cee8 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -111,7 +111,7 @@ Trilinos can be called by loading the module as shown below. The folder names ma module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertex-cfd-installation). +Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertexcfd). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: @@ -261,6 +261,7 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. +
## VERTEX-CFD Installation Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: From a923525e37161c202aca9f92be81b85dc9af9aeb Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 07:59:25 -0500 Subject: [PATCH 180/214] update --- docs/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 625cee8..a64e1a5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -111,7 +111,7 @@ Trilinos can be called by loading the module as shown below. The folder names ma module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertexcfd). +Trilinos and its dependencies are required to compile [VERTEX-CFD](##vertex-cfd-nstallation). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: @@ -261,7 +261,7 @@ make -j install After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. - + ## VERTEX-CFD Installation Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: From bba3995212f2ad9a460256cf51b0caaddebf786d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 08:00:42 -0500 Subject: [PATCH 181/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index a64e1a5..f1c8b43 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -111,7 +111,7 @@ Trilinos can be called by loading the module as shown below. The folder names ma module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -Trilinos and its dependencies are required to compile [VERTEX-CFD](##vertex-cfd-nstallation). +Trilinos and its dependencies are required to compile [VERTEX-CFD](##vertex-cfd-installation). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: From 0254fa0b7204916519b3aea8db0ce63379004b0d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 08:06:04 -0500 Subject: [PATCH 182/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index f1c8b43..8db13d7 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,7 +5,7 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to VERTEX-CFD installation instructions. All of the scripts given in this file is also available in `docs/scripts` directory for users convenience. +VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to VERTEX-CFD installation instructions. All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. ## Trilinos Installation Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have spack based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. From 9db4a765a8d779384c2a575b48ae36a4e03ea02b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 08:10:29 -0500 Subject: [PATCH 183/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 8db13d7..3e689f5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -8,7 +8,7 @@ nav_order: 1 VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to VERTEX-CFD installation instructions. All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. ## Trilinos Installation -Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have spack based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. +Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have [spack](https://spack.readthedocs.io/en/latest/environments.html) based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. ### Trilinos-CPU Installation First of all, create a directory and git clone Spack. From 6463aa44e307819c8263545782751a8d4c33c85c Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 08:12:24 -0500 Subject: [PATCH 184/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 3e689f5..6742e0f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -111,7 +111,7 @@ Trilinos can be called by loading the module as shown below. The folder names ma module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` -Trilinos and its dependencies are required to compile [VERTEX-CFD](##vertex-cfd-installation). +Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertex-cfd-installation). ### Trilinos-GPU Installation VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: From 0169c0959daae7ba18e502808d187e5b3cfe34b6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 08:48:07 -0500 Subject: [PATCH 185/214] update --- docs/contribution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contribution.md b/docs/contribution.md index 1c8a51e..13683a8 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -7,7 +7,7 @@ usemathjax: true # How to contribute to VERTEX-CFD -Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed in the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD please follow the following steps: +Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed in the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD please review the [GitHub documentation on how to collaborate](https://docs.github.com/en/pull-requests) and follow the below steps: - [Git clone VERTEX-CFD repo](https://github.com/ORNL/VERTEX-CFD) and [compile the source code](installation.md). - Create a new branch from the main branch. - Make changes to the source code. @@ -16,6 +16,6 @@ Contribution to VERTEX-CFD source code follows a strict process to warranty robu - Add a description to the pull request with results supporting the changes. - Tag a reviewer to the PR. -A contributor will then review the PR and add comments to the changes. Make sure to address all comments and to respond to all threads. Once the reviewer approves the PR, the branch will be merged to the main branch. +A contributor will then review the PR and add comments to the changes. Make sure to address all comments and to respond to all threads. Once the reviewer resolves all threads and approves the PR, the branch will be merged to the [main branch](https://github.com/ORNL/VERTEX-CFD). If you have any questions or comment, please contact of the main contributors listed in the [home page](../index.md). \ No newline at end of file From eb8dbf2d56d7da27ca594e2eecb98e1b76cea2bb Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 09:15:11 -0500 Subject: [PATCH 186/214] update --- docs/installation.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 6742e0f..8b5afdd 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,13 +5,13 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to VERTEX-CFD installation instructions. All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. +VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to [VERTEX-CFD installation instructions]((#vertex-cfd-installation). All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. ## Trilinos Installation Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have [spack](https://spack.readthedocs.io/en/latest/environments.html) based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. ### Trilinos-CPU Installation -First of all, create a directory and git clone Spack. +First of all, create a directory and git clone spack. ``` mkdir trilinos @@ -19,7 +19,7 @@ cd trilinos/ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git ``` -Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for Spack. Pay special attention to `EDIT` marks locations where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section at the end of the file. +Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for spack. Pay special attention to `EDIT` entries where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section at the end of the file. ``` spack: @@ -85,7 +85,7 @@ spack: - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo ``` -Now, source the Spack setup script and create an environment named vertex. +Now, source the spack setup script and create an environment named vertex. ``` source spack/share/spack/setup-env.sh @@ -108,13 +108,13 @@ spack module tcl refresh Trilinos can be called by loading the module as shown below. The folder names may be different depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on [CADES](https://www.ornl.gov/content/cades) in ORNL, they are located in: ``` -module use //share/spack/modules/linux-centos7-haswell/ +module use //share/spack/modules/linux-centos7-haswell/ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj ``` Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertex-cfd-installation). ### Trilinos-GPU Installation -VERTEX-CFD-GPU installation is not, as of now, supported by the Spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: +VERTEX-CFD-GPU installation is not, as of now, supported by the spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: ``` git clone https://github.com/trilinos/Trilinos.git @@ -259,7 +259,7 @@ Once the configuration is completed, you can build and install Trilinos-16-0-0-G make -j install ``` -After the building and installation, Trilinos-GPU should be ready for VERTEX-CFD installation. +After the building and installation, Trilinos-GPU should be ready for [VERTEX-CFD installation](#vertex-cfd-installation). ## VERTEX-CFD Installation @@ -280,7 +280,7 @@ In the build directory, depending on the CPU/GPU installation, you will need dif ``` #!/bin/bash -install=//share/spack/modules/linux-centos7-haswell/ +install=//share/spack/modules/linux-centos7-haswell/ echo $install module load gcc/13.2.0 @@ -299,8 +299,8 @@ module load googletest module load hdf5 module load zlib-ng module load trilinos/16.0.0-gcc-13.2.0-qzebvtj -HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ -ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ +HDF5_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/hdf5-1.14.5/ +ZLIB_DIR=//opt/spack/install-centos7-haswell-gcc-13.2.0/zlib-ng-2.2.3/ GCC_ROOT=// unset LIBRARY_PATH From 50d968cabe2d6ce5283fbd690cd8fb118db0c5de Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 09:17:19 -0500 Subject: [PATCH 187/214] update --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 8b5afdd..a401871 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,7 +5,7 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to [VERTEX-CFD installation instructions]((#vertex-cfd-installation). All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. +VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to [VERTEX-CFD installation instructions](#vertex-cfd-installation). All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. ## Trilinos Installation Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have [spack](https://spack.readthedocs.io/en/latest/environments.html) based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. From b23c4380560997a4342b64efd7752d1a739e1bbc Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 19 Feb 2025 11:48:53 -0500 Subject: [PATCH 188/214] update --- index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/index.md b/index.md index 23c10f4..0782b01 100644 --- a/index.md +++ b/index.md @@ -26,6 +26,7 @@ We encourage you to contribute to VERTEX-CFD! Please review the [how to contribu - [Jason W. DeGraw](https://www.ornl.gov/staff-profile/jason-w-degraw) - [Doug Stefanski](https://www.ornl.gov/staff-profile/douglas-l-stefanski) - [Filipe L. Brandao](https://www.ornl.gov/staff-profile/filipe-leite-brandao) +- [Ryan Savery](https://impact.ornl.gov/en/persons/ryan-savery) ## Citing If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](DOI_NUMBER) of the version you used as a software citation: From 138e0e4651693bfb7d7525a2ff343d98b5cc1d7b Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Mon, 24 Feb 2025 12:12:08 -0500 Subject: [PATCH 189/214] update --- docs/theory.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index e342694..40e3e53 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -83,7 +83,7 @@ Boundary conditions are weakly imposed by computing numerical flux at the bounda The boundary conditions implemented in VERTEX-CFD are listed below: -- Periodic boundary +- [Periodic boundary](#periodic-boundary) - [Dirichlet with time-transient variation](#dirichlet-boundary) - [Symmetry for isothermal flow](#symmetry-boundary-condition) - [No-slip for viscous flow](#no-slip-boundary-condition) @@ -94,6 +94,9 @@ The boundary conditions implemented in VERTEX-CFD are listed below: The vector solution is denoted by $$U_{bc} = (P_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. +### Periodic boundary +Users can set periodic boundaries in the input file by using the Trilinos specific syntax described in [Panzer_STK class](https://docs.trilinos.org/dev/packages/panzer/doc/html/Panzer__STK__PeriodicBC__Parser_8cpp_source.html). Meshes on periodic faces must be identical for the logic to properly work. + ### Dirichlet boundary The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $T_{bc}$. Linear ramping in time is also available and can be used to vary each primitive variable independently. From 2c86d5e2e7781992455d12db5aea22d284b7b052 Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 11:46:55 -0500 Subject: [PATCH 190/214] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 0782b01..041b9ce 100644 --- a/index.md +++ b/index.md @@ -7,7 +7,7 @@ permalink: / ![Alt text](docs/figures/vertex_cfd_heated_flow_logo.png) -An open-source CFD code for multi-physics modeling and simulation with a focus on fusion applications. +An open-source CFD code for multiphysics modeling and simulation with a focus on fusion applications. {: .fs-6 .fw-300 } [User Guide](docs/index.html){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } From f53b94bd9f06a300d03efbd53a7c9bd70e6bebf1 Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 11:50:28 -0500 Subject: [PATCH 191/214] Update contribution.md --- docs/contribution.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contribution.md b/docs/contribution.md index 13683a8..fa75d5e 100644 --- a/docs/contribution.md +++ b/docs/contribution.md @@ -7,15 +7,15 @@ usemathjax: true # How to contribute to VERTEX-CFD -Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed in the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD please review the [GitHub documentation on how to collaborate](https://docs.github.com/en/pull-requests) and follow the below steps: +Contribution to VERTEX-CFD source code follows a strict process to warranty robustness and deployment on HPC platforms. Any change to the GitHub repo must be reviewed by one of the main developers listed on the [home](../index.md) page. If you are willing to contribute to the development of VERTEX-CFD, please review the [GitHub documentation on how to collaborate](https://docs.github.com/en/pull-requests) and follow the steps given below: - [Git clone VERTEX-CFD repo](https://github.com/ORNL/VERTEX-CFD) and [compile the source code](installation.md). - Create a new branch from the main branch. - Make changes to the source code. - Add/update unit test(s) and/or regression test(s) when needed. - Push changes to your remote branch and create a pull request (PR). -- Add a description to the pull request with results supporting the changes. +- Add a description to the PR with results supporting the changes. - Tag a reviewer to the PR. A contributor will then review the PR and add comments to the changes. Make sure to address all comments and to respond to all threads. Once the reviewer resolves all threads and approves the PR, the branch will be merged to the [main branch](https://github.com/ORNL/VERTEX-CFD). -If you have any questions or comment, please contact of the main contributors listed in the [home page](../index.md). \ No newline at end of file +If you have any questions or comments, please contact of the main contributors listed on the [home page](../index.md). From 8c633a6a0eb30f0be5ca7c3015f118a897aa6f6a Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 11:51:25 -0500 Subject: [PATCH 192/214] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index a80d015..e9fca69 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,7 +6,7 @@ has_children: true --- # VERTEX-CFD v1.0 User Guide -VERTEX-CFD is a free, open source multi physics software for simulations of fusion applications released by Oak Ridge National Laboratory. +VERTEX-CFD is a free, open source multiphysics software for simulations of fusion applications released by Oak Ridge National Laboratory. --- {: .custom } From e54e6b63855a32f8eb87153689d3d72ccf6709a0 Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 12:53:48 -0500 Subject: [PATCH 193/214] Update installation.md --- docs/installation.md | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index a401871..e535295 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,13 +5,13 @@ nav_order: 1 --- # Installation -VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos needs to be compiled with the proper configurations as VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, you can skip to [VERTEX-CFD installation instructions](#vertex-cfd-installation). All of the scripts given in this file is also available in [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for users convenience. +VERTEX-CFD supports both CPU and GPU solvers. Depending on the CPU/GPU supports, Trilinos must be compiled with the proper configurations because VERTEX-CFD relies on the Trilinos installation. For the VERTEX-CFD installation, Trilinos should be available in the system. Installation instructions for both Trilinos and VERTEX-CFD are available. If Trilinos is already built and ready, then you can skip to [VERTEX-CFD installation instructions](#vertex-cfd-installation). All of the scripts given in this file are also available in the [docs/scripts](https://github.com/ORNL/VERTEX-CFD/tree/gh-pages/docs/scripts) directory for the user's convenience. ## Trilinos Installation -Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have [spack](https://spack.readthedocs.io/en/latest/environments.html) based procedure which is easier to follow. For GPU, as of now, Trilinos needs to be built manually. For both options we suggest using a compute node instead of login node as this will be a demanding process. +Trilinos supports both CPU and GPU if configured accordingly. For the installation with CPU support only, we have a [spack](https://spack.readthedocs.io/en/latest/environments.html)-based procedure which is easier to follow. For GPU, as of now, Trilinos must be built manually. For both options, we suggest using a compute node instead of a login node, which would be a demanding process. ### Trilinos-CPU Installation -First of all, create a directory and git clone spack. +First, create a directory and git clone spack. ``` mkdir trilinos @@ -19,7 +19,7 @@ cd trilinos/ git clone -c feature.manyFiles=true --depth=2 https://github.com/spack/spack.git ``` -Next, create a file named `spack-trilinos16.yaml` which should contain the following configuration for spack. Pay special attention to `EDIT` entries where edits may need to be made depending on machine. Modules already available in your machine can be loaded using the `packages` section at the end of the file. +Next, create a file named `spack-trilinos16.yaml` which should contain the configuration presented below for spack. Pay special attention to `EDIT` entries because edits may be required, depending on your machine. Modules already available on your machine can be loaded using the `packages` section at the end of the file. ``` spack: @@ -85,7 +85,7 @@ spack: - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo ``` -Now, source the spack setup script and create an environment named vertex. +Now, source the spack setup script and create an environment named `vertex`. ``` source spack/share/spack/setup-env.sh @@ -94,18 +94,18 @@ spack env activate vertex spack spec ``` - `spack spec` will show the configuration of what is to be installed, make sure the configuration matches what was requested in the configuration file. Next, install Trilinos and its dependencies. + `spack spec` will show the configuration of what is to be installed: make sure the configuration matches what was requested in the configuration file. Next, install Trilinos and its dependencies. ``` spack install ``` -If the session is ended for any reason, the user might need to reactivate the vertex spack env using `spack env activate vertex`. If all compiles without error, the modules are ready to use. `spack location -i trilinos` will return the location of the Trilinos install. The modules can be setup using +If the session is ended for any reason, then the user may need to reactivate the vertex spack env using `spack env activate vertex`. If all compiles without error, then the modules are ready to use. `spack location -i trilinos` will return the location of the Trilinos install. The modules can be set up using ``` spack module tcl refresh ``` -Trilinos can be called by loading the module as shown below. The folder names may be different depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on [CADES](https://www.ornl.gov/content/cades) in ORNL, they are located in: +Trilinos can be called by loading the module as shown below. The folder names may be different, depending on your machine configuration. We suggest finding the current folders instead of copying and pasting. As an example, on [CADES](https://www.ornl.gov/content/cades) in ORNL, they are located in ``` module use //share/spack/modules/linux-centos7-haswell/ @@ -114,7 +114,7 @@ module load trilinos/16.0.0-gcc-13.2.0-qzebvtj Trilinos and its dependencies are required to compile [VERTEX-CFD](#vertex-cfd-installation). ### Trilinos-GPU Installation -VERTEX-CFD-GPU installation is not, as of now, supported by the spack. Hence, it will be required to compile Trilinos with the GPU support manually. For the Trilinos-GPU installation, first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: +As of now, VERTEX-CFD-GPU installation is not supported by the spack. Hence, Trilinos with GPU support must be compiled manually. To install Trilinos-GPU, the first step is to clone Trilinos from the GitHub repo and checkout to `Trilinos-16-0-0` as follows: ``` git clone https://github.com/trilinos/Trilinos.git @@ -122,14 +122,14 @@ cd Trilinos git checkout trilinos-release-16-0-branch ``` -Once you checked out to the correct version, you can create a new folder for the building Trilinos as follows: +Once you have checked out to the correct version, you can create a new folder for building Trilinos, as follows: ``` mkdir build cd build ``` -After that, modules required to build Trilinos need to be loaded. To this purpose, create a file with `trilinos-cuda-env.sh` name. Although the procedure for the module loading changes depend on the machine, as an example, the content of `trilinos-cuda-env.sh` in NERSC Perlmutter is as follows and you can adjust it according to your machine configurations: +Now the modules required to build Trilinos must be loaded. Create a file named `trilinos-cuda-env.sh`. The procedure for module loading differs, depending on the machine. The content of `trilinos-cuda-env.sh` in NERSC Perlmutter is given as an example below: this can be adjusted according to your machine's configurations: ``` #!/bin/bash @@ -167,7 +167,7 @@ export CUDA_MANAGED_FORCE_DEVICE_ALLOC=1 export TPETRA_ASSUME_GPU_AWARE_MPI=0 ``` -Once this is ready, create a configuration script with `trilinos-cuda-config.sh` name and the following content: +Once this is ready, create a configuration script named `trilinos-cuda-config.sh` and include the following content: ``` #!/bin/bash @@ -247,36 +247,36 @@ cmake \ ${TRILINOS_SOURCE_DIR} ``` -As an example, in NERSC Perlmutter system, for `NVCC_WRAPPER`, we used the wrapper available in Sandia National Lab's GitHub page `https://github.com/sandialabs/Albany/blob/master/doc/LandIce/machines/perlmutter/nvcc_wrapper_a100`. After that, run the `trilinos-cuda-config.sh` script as: +As an example, in the NERSC Perlmutter system, for `NVCC_WRAPPER`, we used the wrapper available from the Sandia National Laboratories' GitHub page `https://github.com/sandialabs/Albany/blob/master/doc/LandIce/machines/perlmutter/nvcc_wrapper_a100`. After that, run the `trilinos-cuda-config.sh` script as: ``` ./trilinos-cuda-config.sh ``` -Once the configuration is completed, you can build and install Trilinos-16-0-0-GPU as follows: +Once the configuration is completed, Trilinos-16-0-0-GPU can be built and installed as follows: ``` make -j install ``` -After the building and installation, Trilinos-GPU should be ready for [VERTEX-CFD installation](#vertex-cfd-installation). +After building and installation, Trilinos-GPU should be ready for [VERTEX-CFD installation](#vertex-cfd-installation). ## VERTEX-CFD Installation -Once the Trilinos is installed in your system and ready to use, VERTEX-CFD can be installed. First of all, you can create a folder for VERTEX-CFD and clone from GitHub by using: +Once Trilinos is installed on your system and ready to use, VERTEX-CFD can be installed. First, create a folder for VERTEX-CFD and clone from GitHub by using the following: ``` mkdir VERTEX-CFD cd VERTEX-CFD git clone https://github.com/ORNL/VERTEX-CFD.git ``` -This will clone the VERTEX-CFD folder. In order to build it, create a new folder as build as follows: +This will clone the VERTEX-CFD folder. In order to build VERTEX-CFD, create a new folder, as follows: ``` mkdir build cd build ``` -In the build directory, depending on the CPU/GPU installation, you will need different environment files. For GPU, you can use `trilinos-cuda-config.sh`. There is no need for a separate configuration script. However, for CPU, you will need `vertex-env-cpu.sh`. Those environment scripts are system specific depending and it may change based on your system. Carefully modify them based on your system. The content of the `vertex-env-cpu.sh` is as follows: +Different environment files are needed in the build directory, depending on the CPU/GPU installation. For a GPU, you can use `trilinos-cuda-config.sh`. There is no need for a separate configuration script. However, for a CPU, you will need `vertex-env-cpu.sh`. The environment scripts are system-specific and may vary based on your system. Carefully modify these scripts based on your system. The content of `vertex-env-cpu.sh` is as follows: ``` #!/bin/bash @@ -306,14 +306,14 @@ GCC_ROOT=// unset LIBRARY_PATH ``` -Please note that, those scripts are system specific. Hence you will need to modify them depending on your system. Please carefully modify them and make sure they are loading the correct modules. The scripts that we shared are succesfully used on CADES (CPU configuration) in ORNL and NERSC Perlmutter (GPU configuration). Once you have the environment script, source the script with one of the lines below based on CPU/GPU installation: +Please note that because these scripts are system-specific, they must be modified according to your system. Please carefully modify them and make sure they are loading the correct modules. The scripts shared here are succesfully used on CADES (CPU configuration) in ORNL and NERSC Perlmutter (GPU configuration). Once you have the environment script, source the script with one of the lines below based on the CPU/GPU installation: ``` source vertex-env-cpu.sh source trilinos-cuda-env.sh ``` -Once the environment script is sourced, VERTEX-CFD needs to be configured. Create a configuration script with `vertex-config.sh` name, which contains the following lines: +Once the environment script is sourced, VERTEX-CFD must be configured. Create a configuration script named `vertex-config.sh` containing the following lines: ``` #!/bin/sh @@ -350,22 +350,22 @@ cmake \ \ ${SOURCE} ``` -Please notice that you need to define `NVCC_WRAPPER` and `CMAKE_CXX_COMPILER` for GPU version. Once the configuration script is ready, run it as follows: +Please note that you must define `NVCC_WRAPPER` and `CMAKE_CXX_COMPILER` for the GPU version. Once the configuration script is ready, run it as follows: ``` ./vertex-config.sh ``` -When configured succesfully, you can use make to install as: +When configured succesfully, you can use make to install as follows: ``` make -j install ``` -If you wish to limit the number of cores in the installation, append `-j` flag with desired number of cores such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. Once the installation is completed, unit and regression tests can be run to make sure everything is working as expected. For the instructions for the testing, refer to the following sections. +If you wish to limit the number of cores in the installation, then append `-j` flag with the desired number of cores, such as `-j32`. This will build and install VERTEX-CFD. The binary will be located in `/bin/vertexcfd`. Once the installation is complete, unit and regression tests can be run to ensure that everything is working as expected. For testing instructions, refer to the following sections. # Testing -VERTEX-CFD development procedure strictly requires to develop a unit/regression test for each capability added. Hence, it is suggested to run those tests to make sure installation is properly completed and the functionalities are working as expected. +The VERTEX-CFD development procedure strictly requires that a unit/regression test be developed for each capability added. Hence, it is suggested to run those tests to make sure that installation is properly completed and the functionalities are working as expected. ## Unit Tests -VERTEX-CFD has large amount of unit tests. In order to run those, you can use the below job submission scripts written for SLURM scheduler. +VERTEX-CFD includes a large amount of unit tests. In order to run these tests, you can use the job submission scripts given below which are written for SLURM scheduler. ``` #!/bin/bash @@ -376,7 +376,7 @@ VERTEX-CFD has large amount of unit tests. In order to run those, you can use th #SBATCH --error error.log source /trilinos-cuda-config.sh -#source /vertex-env-cpu.sh # For GPU comment above line and use this line. +#source /vertex-env-cpu.sh # For GPU comment above line, and use this line. export OMP_PROC_BIND=true export OMP_PLACES=threads @@ -389,7 +389,7 @@ export MAP_STRING=slot:pe=${SLURM_CPUS_PER_TASK} ctest -j ${SLURM_NTASKS_PER_NODE} ``` -For other scheduler, such as PBS, you can convert the SLURM submission script. The example output screen for the unit tests is: +For another scheduler, such as PBS, you can convert the SLURM submission script. The example output screen for the unit tests is as follows: ``` Start 1: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_1 Start 2: VertexCFD_KokkosMPI_test_OPENMP_np_1_nt_2 @@ -436,10 +436,10 @@ For other scheduler, such as PBS, you can convert the SLURM submission script. T 191 - VertexCFD_GeometryPrimitives_test_OPENMP_np_192_nt_1 (Failed) 202 - VertexCFD_WriteMatrix_test_OPENMP_np_192_nt_1 (Failed) ``` -Note: several unit tests rely on restart files that are not compatible across architecture. The above list of tests is expected to fail outside of the continuous integration pipelines. +Note: several unit tests rely on restart files that are not compatible across architecture. The listed tests given above are expected to fail outside of the continuous integration pipelines. ## Regression Tests -VERTEX-CFD uses regression tests to make sure the new capabilities does not introduce bugs/errors to the source code. In order to run regression tests, you can use the below submission script written for SLURM scheduler: +VERTEX-CFD uses regression tests to make sure that the new capabilities do not introduce bugs/errors to the source code. In order to run regression tests, you can use the submission script provided below, which is written for SLURM scheduler: ``` #!/bin/bash @@ -450,7 +450,7 @@ VERTEX-CFD uses regression tests to make sure the new capabilities does not intr #SBATCH --job-name=regression-test source /trilinos-cuda-config.sh -#source /vertex-env-cpu.sh # For GPU comment above line and use this line. +#source /vertex-env-cpu.sh # For GPU comment above line, and use this line. export OMP_PROC_BIND=true export OMP_PLACES=threads @@ -464,7 +464,7 @@ cd regression_test pytest -k "test" ``` -The example regression test output is: +The example regression test output is as follows: ``` ============================= test session starts ============================== From 58ed83287250d8afdba18c77a6470b8c2a689924 Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 13:42:52 -0500 Subject: [PATCH 194/214] Update theory.md --- docs/theory.md | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 40e3e53..6d166f1 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -11,7 +11,7 @@ usemathjax: true ## Governing equations -VERTEX-CFD implements the entropically damped artificial compressibility (EDAC) Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (induction-less equation). Coupling between the different equations is ensured by the Buoyancy force and the Lorentz force. +VERTEX-CFD implements the entropically damped artificial compressibility (EDAC) Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (inductionless equation). Coupling between the different equations is ensured by the buoyancy force and the Lorentz force. $$ \begin{align} @@ -46,9 +46,9 @@ $$ ## Discretized equations -VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by the use of L-stable implicit temporal integrator and the use of appropriate mesh density. +VERTEX-CFD employs a finite element discretization method and high-order implicit temporal integrators to integrate partial differential equations (PDEs). Numerical stability of the solution is ensured by using an L-stable implicit temporal integrator and the appropriate mesh density. -A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented in Eq. \ref{eq:pdes}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $$U_h$$ of $$U$$ can be expressed as +A continuous Galerkin finite element method from the Trilinos package, Panzer \cite{panzer-website}, is employed to discretize the equations presented as Eq. \ref{eq:pdes}. Considering a finite dimensional subspace $$V^p_h$$, an approximate solution $$U_h$$ of $$U$$ can be expressed as $$ \begin{equation} @@ -74,7 +74,7 @@ $$ Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section [Boundary conditions](#boundary-conditions). -The time derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. +The time-derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. ## Boundary conditions @@ -95,7 +95,7 @@ The boundary conditions implemented in VERTEX-CFD are listed below: The vector solution is denoted by $$U_{bc} = (P_{p,{bc}}, \mathbf{u}_{bc}, T_{bc}, \varphi_{bc})$$ at the boundary. It should be noted that when the energy equation and the electric potential equation are not solved, the temperature $$T_{bc}$$ and the electric potential $$\varphi_{bc}$$ are ignored. ### Periodic boundary -Users can set periodic boundaries in the input file by using the Trilinos specific syntax described in [Panzer_STK class](https://docs.trilinos.org/dev/packages/panzer/doc/html/Panzer__STK__PeriodicBC__Parser_8cpp_source.html). Meshes on periodic faces must be identical for the logic to properly work. +Users can set periodic boundaries in the input file by using the Trilinos specific syntax described in [Panzer_STK class](https://docs.trilinos.org/dev/packages/panzer/doc/html/Panzer__STK__PeriodicBC__Parser_8cpp_source.html). Meshes on periodic faces must be identical for the logic to work properly. ### Dirichlet boundary The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $T_{bc}$. Linear ramping in time is also available and can be used to vary each primitive variable independently. @@ -114,7 +114,7 @@ $$ $$ ### Symmetry boundary condition -The symmetry boundary condition is a no-penetration condition. The normal component of the fluid velocity to the wall is zero while the tangential component is unrestricted. The same observation is valid for the temeprature gradient as well. Assuming the outward normal vector to a wall boundary is denoted by $$\mathbf{n}_{bc} = \left(n_{bc,x}, n_{bc,y}, n_{bc,z} \right)$$ in a three-dimensional computational domain, the boundary condition for the primitive variables reads: +The symmetry boundary condition is a no-penetration condition. The normal component of the fluid velocity to the wall is zero, whereas the tangential component is unrestricted. The same observation is also valid for the temeprature gradient. Assuming the outward normal vector to a wall boundary is denoted by $$\mathbf{n}_{bc} = \left(n_{bc,x}, n_{bc,y}, n_{bc,z} \right)$$ in a 3D computational domain, the boundary condition for the primitive variables reads as follows: $$ \begin{equation} @@ -129,7 +129,7 @@ $$ \end{equation} $$ -The boundary gradients are function of the interior values: +The boundary gradients are a function of the interior values: $$ \begin{align} @@ -140,7 +140,7 @@ $$ $$ ### No-slip boundary condition -The \emph{no-slip} boundary condition is used to model a viscous flow interacting with a moving wall. The velocity of the flow and the wall are the same which can be put in the mathematical form: +The \emph{no-slip} boundary condition is used to model a viscous flow interacting with a moving wall. The velocity of the flow and the wall are the same: this can be put in the following mathematical form: $$ \begin{equation} @@ -148,7 +148,7 @@ $$ \end{equation} $$ -where $$\mathbf{u_{wall}}$$ is an user input parameter that is defaulted to the zero vector. Since this is a wall, the temperature is set to the wall temperature $$T_{wall}$$. The Lagrange pressure is set to its interior value: +where $$\mathbf{u_{wall}}$$ is a user input parameter that is defaulted to the zero vector. Because this is a wall, the temperature is set to the wall temperature $$T_{wall}$$. The Lagrange pressure is set to its interior value: $$ \begin{equation} @@ -170,11 +170,11 @@ $$ \end{equation} $$ -In VERTEX-CFD the wall boundary velocity $$\mathbf{u_{wall}}$$ can linearly vary as a function of time. This is particularly useful when modeling flow with non-natural initial conditions (non-stationary flow over a stationary obstacle). The wall boundary velocity is set to initially match the flow velocity and linearly decreases to zero with time. This strategy is commonly adopted to ease the work of the numerical solver while developing steady-state flow. +In VERTEX-CFD, the wall boundary velocity $$\mathbf{u_{wall}}$$ can linearly vary as a function of time. This is particularly useful when modeling flow with non-natural initial conditions (non-stationary flow over a stationary obstacle). The wall boundary velocity is set to initially match the flow velocity and linearly decreases to zero with time. This strategy is commonly adopted to ease the work of the numerical solver while developing steady-state flow. ### Rotating wall boundary condition -The \emph{rotating wall} boundary condition is used to model the interaction of a fluid with a rotating wall. The wall in contact with the fluid rotates with an angular velocity $$\omega_w$$ in the XY plane. The Lagrange pressure is set to the interior value, while the fluid velocity is computed from the angular velocity and the fluid temperature set to the wall temperature $$T_{wall}$$ as follows: +The \emph{rotating wall} boundary condition is used to model the interaction of a fluid with a rotating wall. The wall in contact with the fluid rotates with an angular velocity $$\omega_w$$ in the XY plane. The Lagrange pressure is set to the interior value, and the fluid velocity is computed from the angular velocity and the fluid temperature set to the wall temperature $$T_{wall}$$, as follows: $$ \begin{equation} @@ -188,9 +188,9 @@ $$ \end{equation} $$ -The Cartesian coordinates are defined as $$x$$ and $$y$$ and the z-component of the velocity is assumed to be zero when used in three dimension. +The Cartesian coordinates are defined as $$x$$ and $$y$$, and the z-component of the velocity is assumed to be zero when used in 3D. -All boundary gradients are set to the interior values such as: +All boundary gradients are set to the interior values such as the following: $$ \begin{equation} @@ -199,7 +199,7 @@ $$ $$ ### Laminar flow -The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $u_i^{avg}$, the coordinates of the two points on the circle which can be connected by a line that pass through the circle center. The resultant inlet velocity profiles can be calculated as: +The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $u_i^{avg}$, the coordinates of the two points on the circle which can be connected by a line that passes through the circle's center. The resultant inlet velocity profiles can be calculated as follows: $$ \begin{equation} @@ -207,7 +207,7 @@ $$ \end{equation} $$ -where $$R$$ is the characteristic radius of the circle, $$\mathbf{r}$$ is the distance to the circle center which is calculated from the two points provided by the user, and $$C_i$$ is a constant based on the dimensionality of the problem. In 2D, $$C_i$$ is $3/2$, while in 3D, it is $2.0$. This boundary condition can be used with 2D rectangular channels and 3D pipes. The flow direction is calculated based on the surface normals, where the boundary condition is applied. An uniform inlet temperature can also be provided if solving for the temperature equation. The boundary conditions are as follows: +where $$R$$ is the characteristic radius of the circle, $$\mathbf{r}$$ is the distance to the circle center which is calculated from the two points provided by the user, and $$C_i$$ is a constant based on the dimensionality of the problem. In 2D, $$C_i$$ is $3/2$, whereas in 3D, it is $2.0$. This boundary condition can be used with 2D rectangular channels and 3D pipes. The flow direction is calculated based on the surface normals where the boundary condition is applied. A uniform inlet temperature can also be provided if solving for the temperature equation. The boundary conditions are as follows: $$ \begin{equation} @@ -221,7 +221,7 @@ $$ \end{equation} $$ -All boundary gradients are set to the interior values such as: +All boundary gradients are set to interior values such as the following: $$ \begin{equation} @@ -230,7 +230,7 @@ $$ $$ ### Outflow boundary condition -The outflow boundary condition is employed when the back pressure is known at an outlet. The Lagrange pressure is computed from the back pressure $$P_b$$: +The outflow boundary condition is employed when the back pressure at an outlet is known. The Lagrange pressure is computed from the back pressure $$P_b$$: $$ \begin{equation} @@ -238,7 +238,7 @@ $$ \end{equation} $$ -The velocity, the temperature and all boundary gradients are set to their respective interior values such as: +The velocity, the temperature, and all boundary gradients are set to their respective interior values, as follows: $$ \begin{equation} @@ -255,7 +255,7 @@ $$ ### Cavity Lid -A special boundary condition is implemented for the lid-driven cavity case to provide a smooth transition from the lid velocity to the no-slip condition at the wall, as described by Leriche and Gavrilakis \cite{Leriche2000}. The condition assumes that the domain consists of a two- or three-dimensional cube with half width $$h$$, centered on the origin. The Lagrange pressure at the boundary is set to the interior value: +A special boundary condition is implemented for the lid-driven cavity case to provide a smooth transition from the lid velocity to the no-slip condition at the wall, as described by Leriche and Gavrilakis \cite{Leriche2000}. The condition assumes that the domain consists of a 2D or 3D cube with a half width of $$h$$, centered on the origin. The Lagrange pressure at the boundary is set to the interior value: $$ \begin{equation} @@ -263,7 +263,7 @@ $$ \end{equation} $$ -If the energy equation is to be solved, the boundary temperature is set to a specified constant value: +If the energy equation is to be solved, then the boundary temperature is set to a specified constant value: $$ \begin{equation} @@ -271,7 +271,7 @@ $$ \end{equation} $$ -The user is required to specify the index $$n$$ aligned with the boundary normal vector, and the index $$v$$ of the direction in which the velocity is aligned. The velocity components are then defined as: +The user is required to specify the index $$n$$ aligned with the boundary normal vector and the index $$v$$ of the direction in which the velocity is aligned. The velocity components are then defined as follows: $$ \begin{equation} @@ -279,7 +279,7 @@ $$ 0, i \neq v \\ U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 \end{matrix} - \right. + \right, \end{equation} $$ @@ -287,10 +287,10 @@ where $$U_0$$ is the nominal wall velocity, $$N$$ is the number of spatial direc ## Initial conditions -The initial conditions are specified for the velocity $$\mathbf{u}$$, the Lagrange pressure $$P$$ and the temperature $$T$$ when solving for the energy equation. The electric potential $$\varphi$$ is assumed constant and set to $$\varphi_0$$ in all initial conditions presented below. +The initial conditions are specified for the velocity $$\mathbf{u}$$, the Lagrange pressure $$P$$, and the temperature $$T$$ when solving for the energy equation. The electric potential $$\varphi$$ is assumed constant and is set to $$\varphi_0$$ in all initial conditions presented below. ### Uniform initial conditions -The normalized Lagrange pressure, the velocity and the temperature are initialized to constant value across each block of the computational domain: +The normalized Lagrange pressure, the velocity, and the temperature are initialized to constant values across each block of the computational domain: $$ \begin{align} @@ -305,7 +305,7 @@ $$ $$ ### Laminar flow -When using the laminar flow initial condition, the x-component of the velocity is initialized with a parabolic profile. The Lagrange pressure and the temperature are initialized to constant values. The current implementation can be used for two geometries that are a 2D rectangular channel and a 3D pipe. The parabolic profile is function of the average velocity and the channel height $$h$$ in 2D or the pipe diameter $$D$$ in 3D. It is assumed that the flow direction is aligned with the x-axis which yields the following expressions: +When using the laminar flow initial condition, the x-component of the velocity is initialized with a parabolic profile. The Lagrange pressure and the temperature are initialized to constant values. The current implementation can be used for two geometries: a 2D rectangular channel and a 3D pipe. The parabolic profile is function of the average velocity and the channel height $$h$$ in 2D or the pipe diameter $$D$$ in 3D. It is assumed that the flow direction is aligned with the x-axis, which yields the following expressions: $$ \begin{align} @@ -318,4 +318,4 @@ $$ \end{matrix} \right. \end{align} -$$ \ No newline at end of file +$$ From 4e22185e5526b3e3aad27d8edbd6acfdef835157 Mon Sep 17 00:00:00 2001 From: robertsrb Date: Wed, 26 Feb 2025 13:47:30 -0500 Subject: [PATCH 195/214] Update usage.md --- docs/usage.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 7a82429..2a50960 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -7,10 +7,10 @@ usemathjax: true # Usage -Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable and the second one is the input file for the simulation. After the installation, the executable is located in `/bin/vertexcfd`. The input file is case spesific and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. +Once installed, VERTEX-CFD relies on two files. The first one is the 'vertexcfd' executable, and the second one is the input file for the simulation. After the installation, the executable is located in `/bin/vertexcfd`. The input file is case-specific, and there are example case files in `vertex-cfd/examples/inputs`. In this document, the input file located in `vertex-cfd/examples/inputs/incompressible/incompressible_2d_channel.xml` will be used as an example case. ## Running a simulation -In order to run a simulation in serial, vertexcfd can be called directly as: +In order to run a simulation in serial, vertexcfd can be called directly as ``` /bin/vertexcfd --i=PATH_TO_INPUT_FILE/incompressible_2d_channel.xml @@ -31,7 +31,7 @@ export OMP_PLACES=threads mpirun /bin/vertexcfd --i=/incompressible_2d_channel.xml ``` -Once the simulation starts, the example output should look like: +Once the simulation starts, the example output should look like the following: ``` ============================================================================ Time Integration Begin @@ -99,7 +99,7 @@ Thu Mar 31 21:51:51 2022 Time integration complete. ============================================================================ ``` -Once the simulation is completed. The results should be ready for visualization. For the visualization, we suggest ParaView. However, any visualization software that supports Exodus format should work. The example solution file screenshot visualized in Paraview is shown below: +Once the simulation is completed, the results should be ready for visualization. For the visualization, we suggest using ParaView. However, any visualization software that supports Exodus format should work. The example solution file screenshot visualized in Paraview is shown below: ![paraview-ss](https://github.com/user-attachments/assets/75483e4a-cfec-4a51-891f-3b7c72262ba7) From 817aa6ad5448804724731d6da2b8b71f96117828 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 15:13:22 -0500 Subject: [PATCH 196/214] Update theory.md --- docs/theory.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 6d166f1..70860a9 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,7 +14,7 @@ usemathjax: true VERTEX-CFD implements the entropically damped artificial compressibility (EDAC) Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (inductionless equation). Coupling between the different equations is ensured by the buoyancy force and the Lorentz force. $$ -\begin{align} +\begin{aligned} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -24,13 +24,13 @@ $$ \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] \end{matrix} \right. -\end{align} +\end{aligned} $$ The equations are recast in a conservative form and solved for the pressure $$P$$, the velocity $$\mathbf{u}$$, the temperature $$T$$, and the electric potential $$\varphi$$. $$ -\begin{align}\label{eq:pdes} +\begin{aligned}\label{eq:pdes} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -40,7 +40,7 @@ $$ \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] \end{matrix} \right. -\end{align} +\end{aligned} $$ From 9cfa8d6225608d4a12635cb3a42032e0991437fa Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:16:55 -0500 Subject: [PATCH 197/214] update --- docs/theory.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 70860a9..ca20800 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -14,7 +14,7 @@ usemathjax: true VERTEX-CFD implements the entropically damped artificial compressibility (EDAC) Navier-Stokes equations, a temperature equation, and a magneto-hydrodynamics (MHD) equation (inductionless equation). Coupling between the different equations is ensured by the buoyancy force and the Lorentz force. $$ -\begin{aligned} +\begin{align} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -24,7 +24,7 @@ $$ \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] \end{matrix} \right. -\end{aligned} +\end{align} $$ The equations are recast in a conservative form and solved for the pressure $$P$$, the velocity $$\mathbf{u}$$, the temperature $$T$$, and the electric potential $$\varphi$$. @@ -72,7 +72,7 @@ $$ \end{align} $$ -Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method \cite{penaltyMethod}. Implementation of the boundary conditions is further detailed in Section [Boundary conditions](#boundary-conditions). +Boundary fluxes are evaluated at quadrature points, and their contribution is added to the global residuals. The boundary flux $$G$$ is evaluated with the symmetric interior penalty method. Implementation of the boundary conditions is further detailed in Section [Boundary conditions](#boundary-conditions). The time-derivative terms $$\partial_t U$$ are evaluated with a high-order temporal integrator (SDIRK-22 or SDIRK-54) from the Tempus package \cite{tempus-website}. Given a test function $$\phi_i$$ of order $$p$$, integral terms are evaluated with a $$p+1$$ quadrature rule. VERTEX-CFD does not currently implement any numerical method to stabilize the numerical solution and solely relies on the numerical dissipation from the discretization method and the implicit temporal integrator. This strategy has been sufficient for laminar flows, as demonstrated in the following sections. @@ -98,7 +98,7 @@ The vector solution is denoted by $$U_{bc} = (P_{p,{bc}}, \mathbf{u}_{bc}, T_{bc Users can set periodic boundaries in the input file by using the Trilinos specific syntax described in [Panzer_STK class](https://docs.trilinos.org/dev/packages/panzer/doc/html/Panzer__STK__PeriodicBC__Parser_8cpp_source.html). Meshes on periodic faces must be identical for the logic to work properly. ### Dirichlet boundary -The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $T_{bc}$. Linear ramping in time is also available and can be used to vary each primitive variable independently. +The Dirichlet boundary condition denotes the Dirichlet boundary condition in VERTEX-CFD. The velocity is set equal to the user-specified values or Dirichlet values $$\mathbf{u}_D$$ while the Lagrange pressure and the boundary gradients are set to the interior values. The temperature is also set to a user-specified value $$T_{bc}$$. Linear ramping in time is also available and can be used to vary each primitive variable independently. $$ \begin{equation} @@ -207,7 +207,7 @@ $$ \end{equation} $$ -where $$R$$ is the characteristic radius of the circle, $$\mathbf{r}$$ is the distance to the circle center which is calculated from the two points provided by the user, and $$C_i$$ is a constant based on the dimensionality of the problem. In 2D, $$C_i$$ is $3/2$, whereas in 3D, it is $2.0$. This boundary condition can be used with 2D rectangular channels and 3D pipes. The flow direction is calculated based on the surface normals where the boundary condition is applied. A uniform inlet temperature can also be provided if solving for the temperature equation. The boundary conditions are as follows: +where $$R$$ is the characteristic radius of the circle, $$\mathbf{r}$$ is the distance to the circle center which is calculated from the two points provided by the user, and $$C_i$$ is a constant based on the dimensionality of the problem. In 2D, $$C_i$$ is $$3/2$$, whereas in 3D, it is $$2.0$$. This boundary condition can be used with 2D rectangular channels and 3D pipes. The flow direction is calculated based on the surface normals where the boundary condition is applied. A uniform inlet temperature can also be provided if solving for the temperature equation. The boundary conditions are as follows: $$ \begin{equation} @@ -275,8 +275,9 @@ The user is required to specify the index $$n$$ aligned with the boundary normal $$ \begin{equation} - u_{i} \left(\mathbf{r}, t \right) = \left\{ \begin{matrix} - 0, i \neq v \\ +\left\{ + \begin{matrix} + u_{i} \left(\mathbf{r}, t \right) = 0, i \neq v \\ U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 \end{matrix} \right, From 6b2171fbdaff719c54b356ba114ce557e2b04180 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:23:02 -0500 Subject: [PATCH 198/214] update --- docs/theory.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index ca20800..82e66dd 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -30,7 +30,7 @@ $$ The equations are recast in a conservative form and solved for the pressure $$P$$, the velocity $$\mathbf{u}$$, the temperature $$T$$, and the electric potential $$\varphi$$. $$ -\begin{aligned}\label{eq:pdes} +\begin{align}\label{eq:pdes} \left\{ \begin{matrix} \nabla \cdot \mathbf{u} = 0 \\ @@ -40,7 +40,7 @@ $$ \nabla \cdot (\sigma \nabla \varphi) = \nabla \cdot [ \sigma \mathbf{u} \times \mathbf{B^0} ] \end{matrix} \right. -\end{aligned} +\end{align} $$ @@ -277,8 +277,8 @@ $$ \begin{equation} \left\{ \begin{matrix} - u_{i} \left(\mathbf{r}, t \right) = 0, i \neq v \\ - U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 + u_{i} \left(\mathbf{r}, t \right) $=$ 0, i \neq v \\ + U_0 \prod_{j=0, j \neq n}^{N} $=$ \left( 1 - \left(r_i / h \right)^{18} \right)^2 \end{matrix} \right, \end{equation} From d169a0941100bc2d903a4405104ee8d721d503c5 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:35:27 -0500 Subject: [PATCH 199/214] update --- docs/theory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 82e66dd..1576507 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -277,8 +277,8 @@ $$ \begin{equation} \left\{ \begin{matrix} - u_{i} \left(\mathbf{r}, t \right) $=$ 0, i \neq v \\ - U_0 \prod_{j=0, j \neq n}^{N} $=$ \left( 1 - \left(r_i / h \right)^{18} \right)^2 + u_i $=$ \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ + u_{j \neq i} = 0 \end{matrix} \right, \end{equation} From 6e0f9ab4b28a0b886073b9e6c86375f595d3750e Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:38:49 -0500 Subject: [PATCH 200/214] update --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 1576507..e07c3c4 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -284,7 +284,7 @@ $$ \end{equation} $$ -where $$U_0$$ is the nominal wall velocity, $$N$$ is the number of spatial directions, and $$r_i$$ is the distance of the current location from the origin in the $$i^{th}$$ direction. +where $$u_i$$ is the nominal wall velocity and $$r_i$$ is the distance of the current location from the origin in the $$i^{th}$$ direction. ## Initial conditions From 742166ac84da1af91ba3eec3f1f16392a0a338b6 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:43:58 -0500 Subject: [PATCH 201/214] update --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index e07c3c4..12b7cf5 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -280,7 +280,7 @@ $$ u_i $=$ \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ u_{j \neq i} = 0 \end{matrix} - \right, +\right. \end{equation} $$ From 005083c3a5283e957c25891226ffb25ec6bc3c82 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:46:01 -0500 Subject: [PATCH 202/214] update --- docs/theory.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 12b7cf5..4e7f657 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -275,12 +275,11 @@ The user is required to specify the index $$n$$ aligned with the boundary normal $$ \begin{equation} -\left\{ - \begin{matrix} - u_i $=$ \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ - u_{j \neq i} = 0 + u_{i} \left(\mathbf{r}, t \right) = \left\{ \begin{matrix} + 0, i \neq v \\ + U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 \end{matrix} -\right. + \right. \end{equation} $$ From cf90696074a37e8e571fa3b35ac032fa5ce647ac Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:48:02 -0500 Subject: [PATCH 203/214] update --- docs/theory.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/theory.md b/docs/theory.md index 4e7f657..8cc36ba 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -275,11 +275,12 @@ The user is required to specify the index $$n$$ aligned with the boundary normal $$ \begin{equation} - u_{i} \left(\mathbf{r}, t \right) = \left\{ \begin{matrix} - 0, i \neq v \\ - U_0 \prod_{j=0, j \neq n}^{N} \left( 1 - \left(r_i / h \right)^{18} \right)^2 +\left\{ + \begin{matrix} + u_i &=& \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ + u_{j \neq i} &=& 0 \end{matrix} - \right. +\right. \end{equation} $$ From 803e2334cf004be7583cc83800f9ccd334a08ddc Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:50:35 -0500 Subject: [PATCH 204/214] update --- docs/theory.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 8cc36ba..be7aaa1 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -277,7 +277,6 @@ $$ \begin{equation} \left\{ \begin{matrix} - u_i &=& \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ u_{j \neq i} &=& 0 \end{matrix} \right. From e86260cb67a99a1140ad430ee49c4bbd3f6ee753 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 26 Feb 2025 16:59:03 -0500 Subject: [PATCH 205/214] update --- docs/theory.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/theory.md b/docs/theory.md index be7aaa1..8cc36ba 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -277,6 +277,7 @@ $$ \begin{equation} \left\{ \begin{matrix} + u_i &=& \left( 1 - \left(r_i / h \right)^{18} \right)^2 \\ u_{j \neq i} &=& 0 \end{matrix} \right. From af7601b827a444fb6c8ebb5960c29e822fae3ce2 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Tue, 4 Mar 2025 09:19:04 -0500 Subject: [PATCH 206/214] Update installation.md --- docs/installation.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index e535295..c36e0f1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -339,16 +339,28 @@ rm -rf Testing # LD_LIBRARY_PATH appropriately to run jobs. unset LIBRARY_PATH +# EDIT: This command is for CPU. Comment it for GPU. cmake \ -D CMAKE_BUILD_TYPE=${BUILD} \ -D CMAKE_INSTALL_PREFIX=${INSTALL} \ -# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ -D VertexCFD_ENABLE_TESTING=ON \ -D Trilinos_ROOT= \ \ ${SOURCE} + +# EDIT: Uncomment this command for GPU. +#cmake \ +# -D CMAKE_BUILD_TYPE=${BUILD} \ +# -D CMAKE_INSTALL_PREFIX=${INSTALL} \ +# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ +# -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ +# -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ +# -D VertexCFD_ENABLE_TESTING=ON \ +# -D Trilinos_ROOT= \ +# \ +# ${SOURCE} ``` Please note that you must define `NVCC_WRAPPER` and `CMAKE_CXX_COMPILER` for the GPU version. Once the configuration script is ready, run it as follows: ``` From 05045e11c2e1f71539abac2b0ed524834a263110 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Tue, 4 Mar 2025 09:19:55 -0500 Subject: [PATCH 207/214] Update vertex-config.sh --- docs/scripts/vertex-config.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/scripts/vertex-config.sh b/docs/scripts/vertex-config.sh index bf263cd..a16da69 100644 --- a/docs/scripts/vertex-config.sh +++ b/docs/scripts/vertex-config.sh @@ -22,13 +22,25 @@ rm -rf Testing # LD_LIBRARY_PATH appropriately to run jobs. unset LIBRARY_PATH +# EDIT: This command is for CPU. Comment it for GPU. cmake \ -D CMAKE_BUILD_TYPE=${BUILD} \ -D CMAKE_INSTALL_PREFIX=${INSTALL} \ -# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ -D VertexCFD_ENABLE_TESTING=ON \ -D Trilinos_ROOT= \ \ ${SOURCE} + +# EDIT: Uncomment this command for GPU. +#cmake \ +# -D CMAKE_BUILD_TYPE=${BUILD} \ +# -D CMAKE_INSTALL_PREFIX=${INSTALL} \ +# -D CMAKE_CXX_COMPILER=${NVCC_WRAPPER} \ +# -D VertexCFD_ENABLE_COVERAGE_BUILD=OFF \ +# -D CMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic -fdiagnostics-color" \ +# -D VertexCFD_ENABLE_TESTING=ON \ +# -D Trilinos_ROOT= \ +# \ +# ${SOURCE} From d8a28992195dbaf3a2d98e899596aaa45b03f28a Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 7 Mar 2025 10:06:04 -0500 Subject: [PATCH 208/214] update --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 041b9ce..d50f322 100644 --- a/index.md +++ b/index.md @@ -29,7 +29,7 @@ We encourage you to contribute to VERTEX-CFD! Please review the [how to contribu - [Ryan Savery](https://impact.ornl.gov/en/persons/ryan-savery) ## Citing -If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](DOI_NUMBER) of the version you used as a software citation: +If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](https://zenodo.org/records/14907174) of the version you used as a software citation: ```bibtex @software{vertex-cfd, author = {AUTHORS}, From 72a3b03476a65ec5b3c63d426d38c1930082fff4 Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Fri, 7 Mar 2025 10:09:37 -0500 Subject: [PATCH 209/214] update --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index d50f322..5e186f9 100644 --- a/index.md +++ b/index.md @@ -29,7 +29,7 @@ We encourage you to contribute to VERTEX-CFD! Please review the [how to contribu - [Ryan Savery](https://impact.ornl.gov/en/persons/ryan-savery) ## Citing -If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](DOI_NUMBER)](https://zenodo.org/records/14907174) of the version you used as a software citation: +If you use VERTEX-CFD in your work, please cite the Zenodo DOI [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.14907174.svg)](https://doi.org/10.5281/zenodo.14907174) of the version you used as a software citation: ```bibtex @software{vertex-cfd, author = {AUTHORS}, From 1f3c3da47d07d1c1d5348990d9ad8a2751873a49 Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Thu, 13 Mar 2025 08:33:18 -0400 Subject: [PATCH 210/214] Installation instructions are updated for CADES to incorporate recent CADES update. --- docs/installation.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index c36e0f1..fa9f00a 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -30,7 +30,7 @@ spack: build_stage: - $spack/var/spack/spack-stage/build-$arch-$date/ misc_cache: $spack/.cache - build_jobs: 16 # EDIT: change the number with the number of available cores. + build_jobs: 32 install_tree: root: $spack/opt/spack/ projections: @@ -43,7 +43,9 @@ spack: ccache: false db_lock_timeout: 120 package_lock_timeout: null - shared_linking: rpath + shared_linking: + type: rpath + bind: false allow_sgid: true locks: true suppress_gpg_warnings: false @@ -52,7 +54,7 @@ spack: compilers: - compiler: # EDIT: change below with your compiler of choice spec: gcc@13.2.0 - paths: + paths: # EDIT: Change those based on the location of the compiler location you prefer cc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gcc cxx: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/g++ f77: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran @@ -61,28 +63,25 @@ spack: operating_system: centos7 # EDIT: update according to `spack arch`: just the OS version target: x86_64 modules: # EDIT: update with a list of modules you want to load by default - - jobutils/1.0 - - StdEnv - - python/3.11.5 - - openmpi/4.1.0 + - gcc/13.2.0 packages: all: target: [broadwell] # EDIT: Change this according to `spack arch` compiler: [gcc@13.2.0] # EDIT: Change according to your compiler of choice providers: # EDIT: Change versions below according to the default in your machine - mpi: [openmpi] + mpi: [intel-oneapi-mkl] blas: [intel-oneapi-mkl] lapack: [intel-oneapi-mkl] pkgconfig: [pkg-config] - variants: +mpi specs: + - python - ninja - boost - intel-oneapi-mkl - googletest - - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo + - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 ``` Now, source the spack setup script and create an environment named `vertex`. @@ -279,7 +278,7 @@ cd build Different environment files are needed in the build directory, depending on the CPU/GPU installation. For a GPU, you can use `trilinos-cuda-config.sh`. There is no need for a separate configuration script. However, for a CPU, you will need `vertex-env-cpu.sh`. The environment scripts are system-specific and may vary based on your system. Carefully modify these scripts based on your system. The content of `vertex-env-cpu.sh` is as follows: ``` #!/bin/bash - +module use # EDIT: Update this with the module location, as an example: module use spack/share/spack/modules/linux-centos7-broadwell/ install=//share/spack/modules/linux-centos7-haswell/ echo $install From f974e7dafa3f93179299db41408352ea05ce3f4d Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Thu, 13 Mar 2025 08:34:39 -0400 Subject: [PATCH 211/214] Updated scripts to match the instructions. --- docs/scripts/vertex-env-cpu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/scripts/vertex-env-cpu.sh b/docs/scripts/vertex-env-cpu.sh index 4cca807..ccd8b00 100644 --- a/docs/scripts/vertex-env-cpu.sh +++ b/docs/scripts/vertex-env-cpu.sh @@ -1,5 +1,5 @@ #!/bin/bash - +module use # EDIT: Update this with the module location, as an example: module use spack/share/spack/modules/linux-centos7-broadwell/ install=//share/spack/modules/linux-centos7-haswell/ echo $install From 2e52c56bb4e7c002fbff58278a350cf507634d2f Mon Sep 17 00:00:00 2001 From: Furkan Oz Date: Thu, 13 Mar 2025 08:35:20 -0400 Subject: [PATCH 212/214] Update spack-trilinos16.yaml to match with the instructions --- docs/scripts/spack-trilinos16.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/scripts/spack-trilinos16.yaml b/docs/scripts/spack-trilinos16.yaml index 7330f93..3ad1c3d 100644 --- a/docs/scripts/spack-trilinos16.yaml +++ b/docs/scripts/spack-trilinos16.yaml @@ -6,7 +6,7 @@ spack: build_stage: - $spack/var/spack/spack-stage/build-$arch-$date/ misc_cache: $spack/.cache - build_jobs: 16 # EDIT: change the number with the number of available cores. + build_jobs: 32 install_tree: root: $spack/opt/spack/ projections: @@ -19,7 +19,9 @@ spack: ccache: false db_lock_timeout: 120 package_lock_timeout: null - shared_linking: rpath + shared_linking: + type: rpath + bind: false allow_sgid: true locks: true suppress_gpg_warnings: false @@ -28,7 +30,7 @@ spack: compilers: - compiler: # EDIT: change below with your compiler of choice spec: gcc@13.2.0 - paths: + paths: # EDIT: Change those based on the location of the compiler location you prefer cc: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gcc cxx: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/g++ f77: /software/dev_tools/swtree/cs400/gcc/13.2.0/centos7.5_gnu8.5.0/bin/gfortran @@ -37,25 +39,22 @@ spack: operating_system: centos7 # EDIT: update according to `spack arch`: just the OS version target: x86_64 modules: # EDIT: update with a list of modules you want to load by default - - jobutils/1.0 - - StdEnv - - python/3.11.5 - - openmpi/4.1.0 + - gcc/13.2.0 packages: all: target: [broadwell] # EDIT: Change this according to `spack arch` compiler: [gcc@13.2.0] # EDIT: Change according to your compiler of choice providers: # EDIT: Change versions below according to the default in your machine - mpi: [openmpi] + mpi: [intel-oneapi-mkl] blas: [intel-oneapi-mkl] lapack: [intel-oneapi-mkl] pkgconfig: [pkg-config] - variants: +mpi specs: + - python - ninja - boost - intel-oneapi-mkl - googletest - - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 build_type=RelWithDebInfo + - trilinos@16.0.0 +chaco+exodus+hdf5+intrepid2+kokkos+mpi+nox+openmp+panzer+phalanx+shards+shared+stk+tempus+zoltan2 From 39ebb27d1c14d9895ffeae2772ecb0ca5425f87d Mon Sep 17 00:00:00 2001 From: Delchini Marco Date: Wed, 16 Jul 2025 14:21:01 -0400 Subject: [PATCH 213/214] removing exodus files --- examples/CMakeLists.txt | 80 --- ...incompressible_2d_backward_facing_step.xml | 280 --------- .../incompressible_2d_heated_channel.xml | 310 ---------- ...ressible_2d_k_omega_turbulence_channel.xml | 311 ---------- .../incompressible_2d_laminar_airfoil.xml | 286 --------- .../incompressible_2d_tee_junction.xml | 317 ---------- ...ompressible_2d_triangular_cavity_Re100.xml | 275 --------- .../full_induction_mhd/ldc_bl_41x41_30deg.exo | 3 - .../mesh/full_induction_mhd/ldc_bl_81x81.exo | 3 - .../2d-short-channel-h-1-ret-180-yp-0-10.exo | 3 - .../2d_laminar_airfoil_0aoa.exo | 3 - .../2d_laminar_airfoil_3aoa.exo | 3 - .../2d_laminar_airfoil_8aoa.exo | 3 - .../incompressible/2d_triangular_cavity.exo | 3 - regression_test/exodiff_file.txt | 4 - regression_test/pytest.ini | 8 - regression_test/rename_variables_exodus.py.in | 153 ----- ...le_2d_triangular_cavity_Re100_solution.exo | 3 - ...le_2d_triangular_cavity_Re400_solution.exo | 3 - ...le_2d_triangular_cavity_Re800_solution.exo | 3 - .../test_cavity/test_cavity.py | 100 ---- .../test_pipe_flow/exodiff_file.txt | 4 - ...pressible_2d_channel_periodic_solution.exo | 3 - ...ompressible_2d_heated_channel_solution.exo | 3 - ...pressible_3d_channel_periodic_solution.exo | 3 - .../test_pipe_flow/test_pipe_flow.py | 168 ------ .../test_hartmann_problem/exodiff_file.txt | 4 - ...lating_cuda_re_100_ha_100_p_1_solution.exo | 3 - ...lating_cuda_re_100_ha_100_p_2_solution.exo | 3 - ...sulating_cuda_re_100_ha_1_p_1_solution.exo | 3 - ...sulating_cuda_re_100_ha_1_p_2_solution.exo | 3 - ..._insulating_re_100_ha_100_p_1_solution.exo | 3 - ..._insulating_re_100_ha_100_p_2_solution.exo | 3 - ...ic_insulating_re_100_ha_1_p_1_solution.exo | 3 - ...ic_insulating_re_100_ha_1_p_2_solution.exo | 3 - .../test_hartmann_problem.py | 159 ----- .../test_taylor_green_vortex.py | 155 ----- regression_test/vertexcfd_test.py.in | 552 ------------------ 38 files changed, 3229 deletions(-) delete mode 100644 examples/CMakeLists.txt delete mode 100644 examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_heated_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_k_omega_turbulence_channel.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_laminar_airfoil.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_tee_junction.xml delete mode 100644 examples/inputs/incompressible/incompressible_2d_triangular_cavity_Re100.xml delete mode 100644 examples/mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo delete mode 100644 examples/mesh/full_induction_mhd/ldc_bl_81x81.exo delete mode 100644 examples/mesh/incompressible/2d-short-channel-h-1-ret-180-yp-0-10.exo delete mode 100755 examples/mesh/incompressible/2d_laminar_airfoil_0aoa.exo delete mode 100755 examples/mesh/incompressible/2d_laminar_airfoil_3aoa.exo delete mode 100755 examples/mesh/incompressible/2d_laminar_airfoil_8aoa.exo delete mode 100644 examples/mesh/incompressible/2d_triangular_cavity.exo delete mode 100644 regression_test/exodiff_file.txt delete mode 100644 regression_test/pytest.ini delete mode 100644 regression_test/rename_variables_exodus.py.in delete mode 100644 regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re100_solution.exo delete mode 100644 regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re400_solution.exo delete mode 100644 regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re800_solution.exo delete mode 100644 regression_test/test_incompressible/test_cavity/test_cavity.py delete mode 100644 regression_test/test_incompressible/test_pipe_flow/exodiff_file.txt delete mode 100644 regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_periodic_solution.exo delete mode 100644 regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_heated_channel_solution.exo delete mode 100644 regression_test/test_incompressible/test_pipe_flow/gold/incompressible_3d_channel_periodic_solution.exo delete mode 100644 regression_test/test_incompressible/test_pipe_flow/test_pipe_flow.py delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/exodiff_file.txt delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_1_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_2_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_1_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_2_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_1_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_2_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_1_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_2_solution.exo delete mode 100644 regression_test/test_induction_less_mhd_solver/test_hartmann_problem/test_hartmann_problem.py delete mode 100644 regression_test/test_taylor_green_vortex/test_taylor_green_vortex.py delete mode 100644 regression_test/vertexcfd_test.py.in diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt deleted file mode 100644 index 9ce15a4..0000000 --- a/examples/CMakeLists.txt +++ /dev/null @@ -1,80 +0,0 @@ -set(REGRESSION_XML_FILES - inputs/incompressible/incompressible_2d_backward_facing_step.xml - inputs/incompressible/incompressible_2d_channel_periodic.xml - inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml - inputs/incompressible/incompressible_2d_heated_channel.xml - inputs/incompressible/incompressible_2d_k_omega_turbulence_channel.xml - inputs/incompressible/incompressible_2d_laminar_airfoil.xml - inputs/incompressible/incompressible_2d_planar_poiseuille.xml - inputs/incompressible/incompressible_2d_planar_poiseuille_cuda.xml - inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml - inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_channel.xml - inputs/incompressible/incompressible_2d_spalart_allmaras_turbulence_heated_channel.xml - inputs/incompressible/incompressible_2d_standard_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_taylor_green_vortex.xml - inputs/incompressible/incompressible_2d_tee_junction.xml - inputs/incompressible/incompressible_3d_channel_periodic.xml - inputs/incompressible/incompressible_2d_triangular_cavity_Re100.xml - inputs/incompressible/incompressible_3d_wale_cavity.xml - inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml - inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml - inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating.xml - inputs/induction_less_mhd/mhd_2d_hartmann_pb_periodic_insulating_cuda.xml - inputs/full_induction_mhd/full_induction_vortex_2d_pb.xml - inputs/full_induction_mhd/divergence_advection_2d.xml - inputs/full_induction_mhd/current_sheet_2d.xml - inputs/full_induction_mhd/ldc_2d_bx_010.xml - inputs/full_induction_mhd/ldc_2d_mixed_b_050_rotated.xml - ) -file(COPY ${REGRESSION_XML_FILES} DESTINATION .) - -set(REGRESSION_EXO_FILES - mesh/incompressible/2d_backward_facing_step.exo - mesh/incompressible/2d_concentric_convection.exo - mesh/incompressible/2d_concentric_cylinders_rad10.exo - mesh/incompressible/2d_concentric_cylinders_rad20.exo - mesh/incompressible/2d_concentric_cylinders_rad40.exo - mesh/incompressible/2d_concentric_cylinders_rad80.exo - mesh/incompressible/2d_cyclinder_vertex_quad.exo - mesh/incompressible/2d_laminar_airfoil_0aoa.exo - mesh/incompressible/2d_laminar_airfoil_3aoa.exo - mesh/incompressible/2d_laminar_airfoil_8aoa.exo - mesh/incompressible/2d-short-channel-h-1-ret-180-yp-0-10.exo - mesh/incompressible/2d_tee_junction.exo - mesh/incompressible/2d_triangular_cavity.exo - mesh/incompressible/bluntplate_square.exo - mesh/incompressible/pipe_hex.exo - mesh/incompressible/turbulent_channel_mesh_one.exo - mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo - mesh/full_induction_mhd/ldc_bl_81x81.exo - ) -file(COPY ${REGRESSION_EXO_FILES} DESTINATION .) - -install(FILES - inputs/incompressible/incompressible_2d_backward_facing_step.xml - inputs/incompressible/incompressible_2d_channel_periodic.xml - inputs/incompressible/incompressible_2d_concentric_cylinder_convection.xml - inputs/incompressible/incompressible_2d_laminar_airfoil.xml - inputs/incompressible/incompressible_2d_planar_poiseuille.xml - inputs/incompressible/incompressible_2d_realizable_k_epsilon_turbulence_channel.xml - inputs/incompressible/incompressible_2d_rotating_cylinder_viscous.xml - inputs/incompressible/incompressible_2d_tee_junction.xml - inputs/incompressible/incompressible_3d_channel_periodic.xml - inputs/incompressible/incompressible_3d_wale_cavity.xml - inputs/incompressible/incompressible_blunt_plate_laminar_flow_2d.xml - inputs/incompressible/incompressible_oscillating_heated_laminar_flow_2d.xml - DESTINATION examples/incompressible) - -install(FILES - mesh/incompressible/2d_backward_facing_step.exo - mesh/incompressible/2d_concentric_convection.exo - mesh/incompressible/2d_concentric_cylinders_rad40.exo - mesh/incompressible/2d_cyclinder_vertex_quad.exo - mesh/incompressible/2d_laminar_airfoil_0aoa.exo - mesh/incompressible/2d_laminar_airfoil_3aoa.exo - mesh/incompressible/2d_laminar_airfoil_8aoa.exo - mesh/incompressible/2d_tee_junction.exo - mesh/incompressible/bluntplate_square.exo - mesh/incompressible/pipe_hex.exo - DESTINATION examples/incompressible) diff --git a/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml b/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml deleted file mode 100644 index ca163c5..0000000 --- a/examples/inputs/incompressible/incompressible_2d_backward_facing_step.xml +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_heated_channel.xml b/examples/inputs/incompressible/incompressible_2d_heated_channel.xml deleted file mode 100644 index 41ce8a2..0000000 --- a/examples/inputs/incompressible/incompressible_2d_heated_channel.xml +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_k_omega_turbulence_channel.xml b/examples/inputs/incompressible/incompressible_2d_k_omega_turbulence_channel.xml deleted file mode 100644 index c46fa16..0000000 --- a/examples/inputs/incompressible/incompressible_2d_k_omega_turbulence_channel.xml +++ /dev/null @@ -1,311 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_laminar_airfoil.xml b/examples/inputs/incompressible/incompressible_2d_laminar_airfoil.xml deleted file mode 100644 index b8291a8..0000000 --- a/examples/inputs/incompressible/incompressible_2d_laminar_airfoil.xml +++ /dev/null @@ -1,286 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_tee_junction.xml b/examples/inputs/incompressible/incompressible_2d_tee_junction.xml deleted file mode 100644 index 66c08ca..0000000 --- a/examples/inputs/incompressible/incompressible_2d_tee_junction.xml +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/inputs/incompressible/incompressible_2d_triangular_cavity_Re100.xml b/examples/inputs/incompressible/incompressible_2d_triangular_cavity_Re100.xml deleted file mode 100644 index 2f7ed15..0000000 --- a/examples/inputs/incompressible/incompressible_2d_triangular_cavity_Re100.xml +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo b/examples/mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo deleted file mode 100644 index f787569..0000000 --- a/examples/mesh/full_induction_mhd/ldc_bl_41x41_30deg.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a8b99bceed3e3c2a50d3b0f67617ce2da5f234195faa9c6b6a0b07e0365e0915 -size 108692 diff --git a/examples/mesh/full_induction_mhd/ldc_bl_81x81.exo b/examples/mesh/full_induction_mhd/ldc_bl_81x81.exo deleted file mode 100644 index 294e96a..0000000 --- a/examples/mesh/full_induction_mhd/ldc_bl_81x81.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59a13417b2ab7365be74c4e2a266a12846b476e134f16c48033aee3f718f0c32 -size 343060 diff --git a/examples/mesh/incompressible/2d-short-channel-h-1-ret-180-yp-0-10.exo b/examples/mesh/incompressible/2d-short-channel-h-1-ret-180-yp-0-10.exo deleted file mode 100644 index 00a30bd..0000000 --- a/examples/mesh/incompressible/2d-short-channel-h-1-ret-180-yp-0-10.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:40d56305d2e801871f9d6b2b6747791961044665062fdd749be8df3942c7c184 -size 85533 diff --git a/examples/mesh/incompressible/2d_laminar_airfoil_0aoa.exo b/examples/mesh/incompressible/2d_laminar_airfoil_0aoa.exo deleted file mode 100755 index ebd9540..0000000 --- a/examples/mesh/incompressible/2d_laminar_airfoil_0aoa.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e386ffd07c9a0ff97fc16c6043104dbf146bb7c7c92d9952a4c5a74097e42aef -size 5807180 diff --git a/examples/mesh/incompressible/2d_laminar_airfoil_3aoa.exo b/examples/mesh/incompressible/2d_laminar_airfoil_3aoa.exo deleted file mode 100755 index 898c587..0000000 --- a/examples/mesh/incompressible/2d_laminar_airfoil_3aoa.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:28d404461eaa20c2d324b7631fabc706929bfaefe33d32a248e9e8eb8e1f1f4e -size 5802516 diff --git a/examples/mesh/incompressible/2d_laminar_airfoil_8aoa.exo b/examples/mesh/incompressible/2d_laminar_airfoil_8aoa.exo deleted file mode 100755 index 6bee685..0000000 --- a/examples/mesh/incompressible/2d_laminar_airfoil_8aoa.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c3ed907ac09b3aa6be93e6c70ab677eed62c45f5673214b1a36a15058dcc47c -size 5807400 diff --git a/examples/mesh/incompressible/2d_triangular_cavity.exo b/examples/mesh/incompressible/2d_triangular_cavity.exo deleted file mode 100644 index ae981b3..0000000 --- a/examples/mesh/incompressible/2d_triangular_cavity.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8dcadea201bb981a6f483d4fe3921ee581198f61f7a573d9437c10f068aa4f3d -size 485914 diff --git a/regression_test/exodiff_file.txt b/regression_test/exodiff_file.txt deleted file mode 100644 index 6e32eda..0000000 --- a/regression_test/exodiff_file.txt +++ /dev/null @@ -1,4 +0,0 @@ -DEFAULT TOLERANCE relative 1.E-8 absolute 1.E-7 floor 1.E-14 -COORDINATES absolute 1.E-12 -TIME STEPS absolute 1.E-14 -NODAL VARIABLES absolute 1.E-8 diff --git a/regression_test/pytest.ini b/regression_test/pytest.ini deleted file mode 100644 index 3296dd3..0000000 --- a/regression_test/pytest.ini +++ /dev/null @@ -1,8 +0,0 @@ -[pytest] -markers = - push: marks tests that are to be run after pushing to branch (deselect with '-m "not push"') - daily: marks tests that are to be run daily (deselect with '-m "not daily"') - weekly: marks tests that are to be run weekly (deselect with '-m "not weekly"') - incompressible: marks tests for incompressible model. - mhd: marks for mhd model. - gpu: marks for regression tests to run on GPU node. diff --git a/regression_test/rename_variables_exodus.py.in b/regression_test/rename_variables_exodus.py.in deleted file mode 100644 index a2a13c7..0000000 --- a/regression_test/rename_variables_exodus.py.in +++ /dev/null @@ -1,153 +0,0 @@ -# Import Python modules -import sys -import glob -import argparse -import os -from os import path - -# Description: -# This Python script implements logic to rename nodal and element variables -# in Exodus files. The values remain un-changed. The changes in the Exodus -# files are performed in place. Files to update are to be specified at the -# command line. See documentation in the Wiki page under Software quality -# assurance for further details. - -# Adding trilinos folder to the system path and import exodus3 -sys.path.insert( - 0, '@TRILINOS_LIB@') -import exodus3 as exodus - -# Argument parser -parser = argparse.ArgumentParser( - description='Replace field names in Exodus files') -parser.add_argument('-v', - '--verbose', - action='store_true', - help='more verbose output') -parser.add_argument('files', - nargs='+', - help='exodus file(s) to modify', - metavar='file') -parser.add_argument('-n', - '--node', - nargs=2, - action='append', - help='node variable', - metavar=('old-name', 'new-name')) -parser.add_argument('-e', - '--element', - nargs=2, - action='append', - help='element variable', - metavar=('old-name', 'new-name')) -args = parser.parse_args() - -# Assign variable based on parsed arguments -nodal_list = args.node -elem_list = args.element -if nodal_list is None and elem_list is None: - print("\nPlease provide a list of node or element fields to replace.\n") - parser.print_help() - sys.exit() -filename_list = args.files - -# Collect all Exodus files specified at the command line -for f in filename_list: - if not os.path.isfile(f): - sys.exit(f"ERROR: The file '{f}' is not found.") - - -# Verbose function (used when --verbose flag is passed to command line) -def print_verbose(msg): - if args.verbose: - print(msg) - - -# Funtion to replace fields -def replace_field(exo, field_list, field_type): - print_verbose(f"\n\tLoop over {field_type} fields to replace:") - # Set `exo_put_node` function and field type - exo_put_node = lambda: None - if field_type == "nodal": - exo_put_node = exo.put_node_variable_name - field_type = "EX_NODAL" - elif field_type == "element": - exo_put_node = exo.put_element_variable_name - field_type = "EX_ELEM_BLOCK" - else: - exo.close() - sys.exit( - f"\t\tError: Current field type '{field_type}' is not supported. Field type options are 'nodal' or 'element'." - ) - - # Loop over fields to replace - exo_field_list = exo.get_variable_names(field_type) - field_not_found = [] - for old_field, new_field in field_list: - print_verbose(f"\n\t\t\tField to replace: {old_field}...") - found = False - # Look for exact match - if old_field in exo_field_list: - index = exo_field_list.index(old_field) + 1 - exo_put_node(new_field, index) - found = True - print_verbose( - f"\t\t\t...Field {old_field} found with index {index} and replaced with {new_field}." - ) - if not found: - field_not_found.append(old_field) - print_verbose(f"\t\t\t...WARNING: field {old_field} not found.") - if field_not_found: - print_verbose( - f"\n\t\tWARNING: List of field(s) not found in the Exodus file {exo.fileName}:" - ) - for old_field in field_not_found: - print_verbose(f"\t\t- {old_field}") - - # Return list of fields not found - return field_not_found - - -# Loop over file list -full_report = {} -sep = 100 * "*" -for filename in filename_list: - print("\n" + sep) - - # Create exodus object from file - exo = exodus.exodus(filename, mode='a') - - nodal_field_not_found = None - elem_field_not_found = None - - try: - # Nodal fields - if nodal_list is not None: - nodal_field_not_found = replace_field(exo, nodal_list, "nodal") - - # Element fields - if elem_list is not None: - elem_field_not_found = replace_field(exo, elem_list, "element") - finally: - # Close exodus object - exo.close() - - # Store data in report (dictionary) - full_report[filename] = {} - if nodal_field_not_found is not None: - full_report[filename]["nodal"] = nodal_field_not_found - if elem_field_not_found is not None: - full_report[filename]["element"] = elem_field_not_found - -# Print full report -print("\n" + sep) -print("Full report:") -for filename in filename_list: - print(f"\tFile name: {filename}") - for key, values in full_report[filename].items(): - if values: - print("\t\t" + key + " variables not updated:") - for v in values: - print(f"\t\t - {v}") - else: - print("\t\tall " + key + " variables updated.") diff --git a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re100_solution.exo b/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re100_solution.exo deleted file mode 100644 index 4f57cb5..0000000 --- a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re100_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9291bb12ce740d707a9cab981467d99678af8ff088fe5e3ee007053e9bf53852 -size 1274912 diff --git a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re400_solution.exo b/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re400_solution.exo deleted file mode 100644 index 28e175b..0000000 --- a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re400_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f58f9193002e8726c3fb3d66bc5d87b0c6f5767f76aa2dff9824f38693a5071 -size 1274912 diff --git a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re800_solution.exo b/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re800_solution.exo deleted file mode 100644 index 886c330..0000000 --- a/regression_test/test_incompressible/test_cavity/gold/incompressible_2d_triangular_cavity_Re800_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7077218589ce653a2a0f8f3cc7f3c775c4511c392588bb3890b451097bce68c9 -size 1274912 diff --git a/regression_test/test_incompressible/test_cavity/test_cavity.py b/regression_test/test_incompressible/test_cavity/test_cavity.py deleted file mode 100644 index 1403c4b..0000000 --- a/regression_test/test_incompressible/test_cavity/test_cavity.py +++ /dev/null @@ -1,100 +0,0 @@ -import sys -import os -import pytest - -import vertexcfd_test - - -class TestCavity: - # setup method for class - @classmethod - def setup_class(self): - """ setup any state specific to the execution of the - given class (which usually contains tests).""" - print('\n***************** Setup class method *******************') - - # Class variables (to not change) - self.mesh_file_path = vertexcfd_test.mesh_file_path - self.input_file_path = vertexcfd_test.input_file_path - self.vertexcfd_exec = vertexcfd_test.vertexcfd_exec - self.exodiff_exec = vertexcfd_test.exodiff_exec - self.main_path = vertexcfd_test.main_path - self.no_exodiff_file = None - - # Change working directory to test directory - self.test_path = os.path.dirname(os.path.realpath(__file__)) - os.chdir(self.test_path) - - # teardown method for class - @classmethod - def teardown_class(self): - """ teardown any state that was previously - setup with a call to setup_class.""" - print('\n****************** Teardown class method ******************') - # Change working directory back to main directory - os.chdir(vertexcfd_test.main_path) - - # setup method for each function - def setup_method(self): - """ setup test.""" - print('\nSetup method') - # Initialize input file, mesh file and output file names - self.input_file = None - self.mesh_file = None - self.output_file = None - - # teardown method for each function - def teardown_method(self): - """ teardown test.""" - print('\nTeardown method') - # Delete input file, mesh file and output file - vertexcfd_test.clean_working_directory(self) - - ############################################################################# - ############################################################################# - ############################################################################# - @pytest.mark.push - @pytest.mark.daily - @pytest.mark.weekly - @pytest.mark.incompressible - def test_2d_triangular_cavity(self, request): - # Parameters - mark_expr = request.config.option.markexpr - vertexcfd_options = () - copy_xml_file = False - - # default parameters to be used on 'push' - base_input_file = "incompressible_2d_triangular_cavity" - self.input_file = base_input_file + "_Re100.xml" - source_input_file = self.input_file - output_name = base_input_file + "_Re100_solution.exo" - nu = 0.8 - - if ("daily" in mark_expr): - self.input_file = base_input_file + "_Re400.xml" - output_name = base_input_file + "_Re400_solution.exo" - nu = 0.2 - elif ("weekly" in mark_expr): - self.input_file = base_input_file + "_Re800.xml" - output_name = base_input_file + "_Re800_solution.exo" - nu = 0.1 - - xml_args_to_replace_list = [] - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Kinematic viscosity', nu)) - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Exodus Output File', - output_name)) - - vertexcfd_test.create_xml_file_from_base_file( - self, source_input_file, xml_args_to_replace_list) - - # Parameters for exodiff executables - exodiff_options = () - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options, - copy_xml_file) diff --git a/regression_test/test_incompressible/test_pipe_flow/exodiff_file.txt b/regression_test/test_incompressible/test_pipe_flow/exodiff_file.txt deleted file mode 100644 index fb64a95..0000000 --- a/regression_test/test_incompressible/test_pipe_flow/exodiff_file.txt +++ /dev/null @@ -1,4 +0,0 @@ -DEFAULT TOLERANCE relative 1.E-8 absolute 1.E-7 floor 1.E-14 -COORDINATES absolute 1.E-12 -TIME STEPS absolute 1.E-8 -NODAL VARIABLES absolute 1.E-8 diff --git a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_periodic_solution.exo b/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_periodic_solution.exo deleted file mode 100644 index d8b3664..0000000 --- a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_channel_periodic_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a4fa171ad1cb4cb9bc294c82c08c75b6642abdf2d0e0dc26d449e92ac23a5827 -size 688136 diff --git a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_heated_channel_solution.exo b/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_heated_channel_solution.exo deleted file mode 100644 index 04b556f..0000000 --- a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_2d_heated_channel_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:829352490118d2033883d361f46406a73f65b7c153dae48d80c9e18f3476cd9b -size 932072 diff --git a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_3d_channel_periodic_solution.exo b/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_3d_channel_periodic_solution.exo deleted file mode 100644 index 37abcf7..0000000 --- a/regression_test/test_incompressible/test_pipe_flow/gold/incompressible_3d_channel_periodic_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea85bc3f661ac006dd92a7ac8caf972e8bcf4811fec3d349a0f5f9d43cf39809 -size 4714436 diff --git a/regression_test/test_incompressible/test_pipe_flow/test_pipe_flow.py b/regression_test/test_incompressible/test_pipe_flow/test_pipe_flow.py deleted file mode 100644 index 68e5b11..0000000 --- a/regression_test/test_incompressible/test_pipe_flow/test_pipe_flow.py +++ /dev/null @@ -1,168 +0,0 @@ -import sys -import os -import pytest -import re - -import vertexcfd_test - - -class TestPipeFlow: - # setup method for class - @classmethod - def setup_class(self): - """ setup any state specific to the execution of the - given class (which usually contains tests).""" - print('\n***************** Setup class method *******************') - - # Class variables (to not change) - self.mesh_file_path = vertexcfd_test.mesh_file_path - self.input_file_path = vertexcfd_test.input_file_path - self.vertexcfd_exec = vertexcfd_test.vertexcfd_exec - self.exodiff_exec = vertexcfd_test.exodiff_exec - self.main_path = vertexcfd_test.main_path - self.no_exodiff_file = None - - # Change working directory to test directory - self.test_path = os.path.dirname(os.path.realpath(__file__)) - os.chdir(self.test_path) - - # teardown method for class - @classmethod - def teardown_class(self): - """ teardown any state that was previously - setup with a call to setup_class.""" - print('\n****************** Teardown class method ******************') - # Change working directory back to main directory - os.chdir(vertexcfd_test.main_path) - - # setup method for each function - def setup_method(self): - """ setup test.""" - print('\nSetup method') - # Initialize input file, mesh file and output file names - self.input_file = None - self.mesh_file = None - self.output_file = None - - # teardown method for each function - def teardown_method(self): - """ teardown test.""" - print('\nTeardown method') - # Delete input file, mesh file and output file - vertexcfd_test.clean_working_directory(self) - - ############################################################################# - ############################################################################# - ############################################################################# - - # Check inlet conditions (velocity and temperature) - def check_inlet_conditions(self, capfd): - # Get captured test output for parsing - fd_out, fd_err = capfd.readouterr() - - # Let pytest re-capture the output - sys.stdout.write(fd_out) - sys.stderr.write(fd_err) - - pattern = re.compile(' Inlet - velocity_0 = (.+)\n') - vels = [float(t) for t in pattern.findall(fd_out)] - pattern = re.compile(' Inlet - temperature = (.+)\n') - temps = [float(t) for t in pattern.findall(fd_out)] - - # Expected velocity and temperature values - vel_exp = [0.000600000000, 0.000600000000] - temp_exp = [4.0, 4.047127939785872] - - # Assert values - assert len(vels) == 2 - assert len(temps) == 2 - - for i in range(0, 2): - assert vels[i] == pytest.approx(vel_exp[i], rel=1.0e-8) - assert temps[i] == pytest.approx(temp_exp[i], rel=1.0e-8) - - # Check probe values - def check_probe_values(self, capfd): - # Get captured test output for parsing - fd_out, fd_err = capfd.readouterr() - - # Let pytest re-capture the output - sys.stdout.write(fd_out) - sys.stderr.write(fd_err) - - pattern = re.compile(' Probe Upper 1 - temperature = (.+)\n') - pb1 = [float(t) for t in pattern.findall(fd_out)] - pattern = re.compile(' Probe Upper 2 - temperature = (.+)\n') - pb2 = [float(t) for t in pattern.findall(fd_out)] - pattern = re.compile(' Probe Right 1 - temperature = (.+)\n') - pb3 = [float(t) for t in pattern.findall(fd_out)] - - # Expected velocity and temperature values - pb1_exp = [20.0, 20.65956051875808] - pb2_exp = [20.0, 20.03234994314487] - pb3_exp = [ - 20.0, 20.00184327050597, 20.00184327047455, 20.00184327047455 - ] - - # Assert values - assert len(pb1) == 2 - assert len(pb2) == 2 - assert len(pb3) == 4 - - for i in range(0, 2): - assert pb1[i] == pytest.approx(pb1_exp[i], rel=1.0e-8) - assert pb2[i] == pytest.approx(pb2_exp[i], rel=1.0e-8) - - for i in range(0, 4): - assert pb3[i] == pytest.approx(pb3_exp[i], rel=1.0e-8) - - @pytest.mark.push - @pytest.mark.incompressible - def test_2d_flow(self): - #### Parameters to edit for each function #### - # Parameters for VertexCFD run - self.input_file = "incompressible_2d_channel_periodic.xml" - vertexcfd_options = () - - # Parameters for exodiff executables - exodiff_options = () - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options) - - @pytest.mark.push - @pytest.mark.incompressible - def test_2d_heated_flow(self, capfd): - #### Parameters to edit for each function #### - # Parameters for VertexCFD run - self.input_file = "incompressible_2d_heated_channel.xml" - vertexcfd_options = () - - # Parameters for exodiff executables - exodiff_options = () - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options) - - # Check surface-averaged values - self.check_inlet_conditions(capfd) - - # Check probe values - self.check_probe_values(capfd) - - @pytest.mark.daily - @pytest.mark.incompressible - def test_3d_flow(self): - #### Parameters to edit for each function #### - # Parameters for VertexCFD run - self.input_file = "incompressible_3d_channel_periodic.xml" - vertexcfd_options = () - - # Parameters for exodiff executables - exodiff_options = () - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options) diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/exodiff_file.txt b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/exodiff_file.txt deleted file mode 100644 index f382805..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/exodiff_file.txt +++ /dev/null @@ -1,4 +0,0 @@ -DEFAULT TOLERANCE relative 1.E-8 absolute 1.E-7 floor 1.E-14 -COORDINATES absolute 1.E-12 -TIME STEPS absolute 1.E-10 -NODAL VARIABLES absolute 1.E-8 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_1_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_1_solution.exo deleted file mode 100644 index 80a7a26..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_1_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19687bf2f2317bbef602e82ce76be7e3dff391a0ade0127afff2bbb8b04a2eeb -size 3054729 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_2_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_2_solution.exo deleted file mode 100644 index 67891bb..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_100_p_2_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bdd5575b572639601a54a0fe835bee7f560472c0cd0e93120467eda6e7c3e518 -size 3054729 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_1_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_1_solution.exo deleted file mode 100644 index cc54b94..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_1_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:44fee22de9a33ff53e7eab978f0fe3d3f29451e8f73188c36f61644914941d1b -size 120329 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_2_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_2_solution.exo deleted file mode 100644 index 6faad60..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_cuda_re_100_ha_1_p_2_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42c1b54b1ac7dafb722b90d788a6a07421447499795fe90c0ba71b9ac16a63bd -size 120329 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_1_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_1_solution.exo deleted file mode 100644 index 2f8addb..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_1_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b152c425f7ac87cbeae91e73a119216b346ea3ee069e536a993bf246094817db -size 3326668 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_2_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_2_solution.exo deleted file mode 100644 index 289a411..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_100_p_2_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d23e3e20a1bc59acee9969cc95f93963ff3c9760605bb98d886a442ce000994 -size 3326668 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_1_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_1_solution.exo deleted file mode 100644 index 895759b..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_1_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c5cc41cfac806cecee3735729ce01e80974ee6ab0c7b4f9fb5f526dc67b7d938 -size 129819 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_2_solution.exo b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_2_solution.exo deleted file mode 100644 index fceb4b6..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/gold/mhd_2d_hartmann_pb_periodic_insulating_re_100_ha_1_p_2_solution.exo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:29fa41c77db59a2b854d1ec9c4fbf5afb7bf0388e8734fa07a9441c6dbc82054 -size 129819 diff --git a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/test_hartmann_problem.py b/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/test_hartmann_problem.py deleted file mode 100644 index b7ad072..0000000 --- a/regression_test/test_induction_less_mhd_solver/test_hartmann_problem/test_hartmann_problem.py +++ /dev/null @@ -1,159 +0,0 @@ -import sys -import os -import pytest - -import vertexcfd_test - - -class TestHartmannProblem: - # setup method for class - @classmethod - def setup_class(self): - """ setup any state specific to the execution of the - given class (which usually contains tests).""" - print('\n***************** Setup class method *******************') - - # Class variables (to not change) - self.mesh_file_path = vertexcfd_test.mesh_file_path - self.input_file_path = vertexcfd_test.input_file_path - self.vertexcfd_exec = vertexcfd_test.vertexcfd_exec - self.exodiff_exec = vertexcfd_test.exodiff_exec - self.main_path = vertexcfd_test.main_path - self.no_exodiff_file = None - - # Change working directory to test directory - self.test_path = os.path.dirname(os.path.realpath(__file__)) - os.chdir(self.test_path) - - # teardown method for class - @classmethod - def teardown_class(self): - """ teardown any state that was previously - setup with a call to setup_class.""" - print('\n****************** Teardown class method ******************') - # Change working directory back to main directory - os.chdir(vertexcfd_test.main_path) - - # setup method for each function - def setup_method(self): - """ setup test.""" - print('\nSetup method') - # Initialize input file, mesh file and output file names - self.input_file = None - self.mesh_file = None - self.output_file = None - - # teardown method for each function - def teardown_method(self): - """ teardown test.""" - print('\nTeardown method') - # Delete input file, mesh file and output file - vertexcfd_test.clean_working_directory(self) - - ############################################################################# - ############################################################################# - ############################################################################# - @pytest.mark.push - @pytest.mark.weekly - @pytest.mark.mhd - @pytest.mark.gpu - def test_2d_hartmann_problem(self, request): - #### Parameters to edit for each function #### - # CPU or GPU - run_gpu = False - markers = request.config.option.markexpr.split() - if ('gpu' in markers): - run_gpu = True - - # Set polynomial order based on marker: p = 1 on push and gpu, and p = 2 on weekly - p = 1 - ha = 1 - if ('gpu' in markers and 'push' in markers): - p = 2 - ha = 1 - elif ('gpu' in markers and 'weekly' in markers): - p = 1 - ha = 100 - elif ('weekly' in markers): - p = 2 - ha = 100 - - # Parameters for VertexCFD run - vertexcfd_options = () - - # Input file and output file - copy_xml_file = False - base_input_file = "mhd_2d_hartmann_pb_periodic_insulating" - if run_gpu: - base_input_file += "_cuda" - source_input_file = base_input_file + ".xml" - name = "_re_100_ha_" + str(ha) + "_p_" + str(p) - base_input_file = source_input_file.split(".xml")[0] - self.input_file = base_input_file + name + ".xml" - output_name = base_input_file + name + "_solution.exo" - - # Set parameter values - momentum_source = None - ext_magn_field = None - nx = None - ny = None - if ha == 1: - momentum_source = "{0.04194528049, 0.0}" - ext_magn_field = "{0.0, 0.1, 0.0}" - nx = 8 - ny = 32 - elif ha == 100: - momentum_source = "{101.010101, 0.0}" - ext_magn_field = "{0.0, 10.0, 0.0}" - nx = 64 - ny = 256 - - # Update parameters in input file - xml_args_to_replace_list = [] - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Exodus Output File', - output_name)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Momentum Source', - momentum_source)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace( - 'Parameter', 'name', 'External Magnetic Field Value', - ext_magn_field)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'X Elements', nx)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Y Elements', ny)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Basis Order', p)) - - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Integration Order', 2 * p)) - - vertexcfd_test.create_xml_file_from_base_file(self, - source_input_file, - xml_args_to_replace_list, - restart=False) - - # Parameters for exodiff executables - exodiff_options = () - - #### The following code should not be modified #### - # Run VertexCFD and call exodiff to compare to gold file - vertexcfd_test.run_test(self, - vertexcfd_options, - exodiff_options, - copy_xml_file, - run_gpu=run_gpu) diff --git a/regression_test/test_taylor_green_vortex/test_taylor_green_vortex.py b/regression_test/test_taylor_green_vortex/test_taylor_green_vortex.py deleted file mode 100644 index 8a84e9e..0000000 --- a/regression_test/test_taylor_green_vortex/test_taylor_green_vortex.py +++ /dev/null @@ -1,155 +0,0 @@ -import sys -import os -import pytest - -import vertexcfd_test - - -class TestTaylorGreenVortex: - # setup method for class - @classmethod - def setup_class(self): - """ setup any state specific to the execution of the - given class (which usually contains tests).""" - print('\n***************** Setup class method *******************') - - # Class variables (to not change) - self.mesh_file_path = vertexcfd_test.mesh_file_path - self.input_file_path = vertexcfd_test.input_file_path - self.vertexcfd_exec = vertexcfd_test.vertexcfd_exec - self.exodiff_exec = vertexcfd_test.exodiff_exec - self.main_path = vertexcfd_test.main_path - self.no_exodiff_file = None - - # Change working directory to test directory - self.test_path = os.path.dirname(os.path.realpath(__file__)) - os.chdir(self.test_path) - - # Gold values for L1/L2 error norms - self.ref_error_norms = [[ - None, - [ - 3.1354940494154192e-03, 4.8827471008265188e-03, - 4.8827471008264806e-03 - ], - [ - 6.1898331388840358e-04, 9.5931336430924428e-04, - 9.5931336430924038e-04 - ] - ], - [ - None, - [ - 7.2363659991042098e-04, - 1.0898079401035921e-03, - 1.0898079401035173e-03 - ], - [ - 1.4131616203283908e-04, - 2.1968921759816913e-04, - 2.1968921759815726e-04 - ] - ], - [ - None, - [ - 1.1253018483846534e-04, - 1.8455771918065726e-04, - 1.8455771918063306e-04 - ], - [ - 2.2227805982151793e-05, - 4.0783845770510348e-05, - 4.0783845770502603e-05 - ] - ]] - - # teardown method for class - @classmethod - def teardown_class(self): - """ teardown any state that was previously - setup with a call to setup_class.""" - print('\n****************** Teardown class method ******************') - # Change working directory back to main directory - os.chdir(vertexcfd_test.main_path) - - # setup method for each function - def setup_method(self): - """ setup test.""" - print('\nSetup method') - # Initialize input file, mesh file and output file names - self.input_file = None - self.mesh_file = None - self.output_file = None - - # teardown method for each function - def teardown_method(self): - """ teardown test.""" - print('\nTeardown method') - # Delete input file, mesh file and output file - #vertexcfd_test.clean_working_directory(self) - - ############################################################################# - ############################################################################# - ############################################################################# - @pytest.mark.push - @pytest.mark.daily - @pytest.mark.weekly - @pytest.mark.incompressible - def test_2d_mesh_convergence_laminar(self, request, capfd): - #### Parameters to edit for each function #### - # Parameters for VertexCFD run - copy_xml_file = False - restart = False - exodiff_options = None - base_input_file = "incompressible_2d_taylor_green_vortex" - final_time = 50 - source_input_file = base_input_file + ".xml" - - # Parameters for mesh convergence study - mark_expr = request.config.option.markexpr - xy_list = [20] - if (mark_expr == 'weekly'): - xy_list = [20, 40] - elif (mark_expr == 'weekly'): - xy_list = [20, 40, 80] - errors = [] - - # Loop over all elements of 'xy_list' - for i in range(0, len(xy_list)): - xy = xy_list[i] - self.input_file = base_input_file + "_" + str( - final_time) + "_" + str(final_time) + "_" + str(xy) + ".xml" - output_name = base_input_file + "_" + str(final_time) + "_" + str( - xy) + "_solution.exo" - - xml_args_to_replace_list = [] - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'X Elements', xy)) - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Y Elements', xy)) - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Final Time', final_time)) - xml_args_to_replace_list.append( - vertexcfd_test.xml_args_to_replace('Parameter', 'name', - 'Exodus Output File', - output_name)) - - vertexcfd_test.create_xml_file_from_base_file( - self, source_input_file, xml_args_to_replace_list, restart) - - # Clear replacing parameter list - xml_args_to_replace_list.clear() - - vertexcfd_options = () - - # Run VertexCFD (note that there is no comparison to gold file here) - vertexcfd_test.run_test(self, vertexcfd_options, exodiff_options, - copy_xml_file) - - # Compare error norms to gold values - vertexcfd_test.compare_error_norms(self.ref_error_norms[i], capfd, - 1.0e-10) diff --git a/regression_test/vertexcfd_test.py.in b/regression_test/vertexcfd_test.py.in deleted file mode 100644 index 622b21e..0000000 --- a/regression_test/vertexcfd_test.py.in +++ /dev/null @@ -1,552 +0,0 @@ -import os -import sys -import shutil -import subprocess -import glob -import xml.etree.ElementTree as ET -from collections import namedtuple -import json -import math -import pytest -import re - -############################################## -############## Global variables ############## -############################################## - -#### User dependent variables #### -# List objects to store executables and paths -exec_list_check = [] -path_list_check = [] - -# Absolute path with exodiff executable (Make sure to use the same Trilinos -# path as in the CMake file for consistency), and path to Workbench directory -exodiff_exec = "@EXODIFF_PATH@" -exec_list_check.append(exodiff_exec) - -### Variables that should not be modified #### - -# Main working directory -main_path = os.path.dirname(os.path.realpath(__file__)) -path_list_check.append(main_path) -parent_main_path = os.path.normpath(os.path.join(main_path, os.pardir)) - -# Absolute path with mesh files -mesh_file_path = "examples" -mesh_file_path = os.path.join(parent_main_path, mesh_file_path) -path_list_check.append(mesh_file_path) - -# Absolute path with input files -input_file_path = "examples" -input_file_path = os.path.join(parent_main_path, input_file_path) -path_list_check.append(input_file_path) - -# Absolute path with VertexCFD executable -vertexcfd_exec = os.path.join(parent_main_path, "src/vertexcfd") -exec_list_check.append(vertexcfd_exec) - -# Declare tuples that is used to substitute values in XML files. -# Defaults are give right-to-left, so optional entries appear on -# the right and 'parent' is the only optional entry. -xml_args_to_replace = namedtuple( - 'xml_args_to_replace', - ('tag', 'attribute', 'attribute_entry', 'new_value', 'parent'), - defaults=('', )) - -# Check if all executables are valid -for exec in exec_list_check: - if not os.access(exec, os.X_OK): - print(f"Error: executable {exec} is not valid.") - -# Check if all paths are valid -for d in path_list_check: - if not os.path.exists(d): - raise FileNotFoundError(f"{d} folder does not exist!") - -# Output variables -print( - "\n\n============================= Global variables =============================" -) -print("Mesh file absolute path: ", mesh_file_path) -print("Input file absolute path: ", input_file_path) -print("VertexCFD executable absolute path: ", vertexcfd_exec) -print("Exodiff executable absolute path: ", exodiff_exec) -print( - "=============================================================================\n" -) - -############################################## -############## Global functions ############## -############################################## - - -# Search a value in a XML file and return value if found, 'None' else. -def search_value_through_xml_file(xml, tag, attribute, key_word): - # find all tags with key word 'key_word' - keyword_list = xml.findall(f'.//{tag}[@{attribute}="{key_word}"]') - # Extract all entries and store them in a list to return. If empty, - # 'None' is returned. - if keyword_list: - return [entry.get('value') for entry in keyword_list] - - -# Find all tags with attribute="value" and set new_attribute="new_value". -# If parent is provided, only find tags within a with name="parent". -def modify_xml_tag(xml, - name, - attribute, - value, - new_attribute, - new_value, - parent=''): - """ - Find all tags with attribute="value" and set new_attribute="new_value". - If parent is provided, only find tags within a with name="parent". - """ - if parent: - parent = f'ParameterList[@name="{parent}"]/' - tags = xml.findall(f'.//{parent}{name}[@{attribute}="{value}"]') - if tags: - for tag in tags: - tag.set(new_attribute, str(new_value)) - else: - raise Exception(f'Tag <{tag}> with {attribute}="{value}" not found.') - - -# Read XML file -def read_xml_from_file(file_name): - return ET.parse(file_name) - - -# Write XML data to a XML file -def write_xml_to_file(xml, file_name): - xml.write(file_name) - - -# To handle restart capabilities: this function will set the `Read Restart File` to True -# and will add the restart file to the XML input file to read from. -def read_restart_file(self, xml_args_to_replace_list): - restart_dir = "restart" - # Look for a `restart_dir` directory in the test directory - if not os.path.isdir(restart_dir): - msg = "The restart directory {} could not be find in the test directory." - sys.exit(msg.format(restart_dir)) - - # Check if the `restart_dir` directory contains the restart files needed - base = self.input_file.split(".xml")[0] - data_file = base + "_read.data" - dofmap_file = base + "_read.dofmap" - if not os.path.isfile(os.path.join(restart_dir, data_file)): - msg = "The restart directory does not contain the restart file {}." - sys.exit(msg.format(data_file)) - if not os.path.isfile(os.path.join(restart_dir, dofmap_file)): - msg = "The restart directory does not contain the restart file {}." - sys.exit(msg.format(dofmap_file)) - - # Set `Read Restart File` to `True` - xml_args_to_replace_list.append( - xml_args_to_replace('Parameter', 'name', 'Read Restart', 'true')) - - # Append parameters to the list of entries to modify in the XML file - data_file_path = os.path.join(restart_dir, data_file) - xml_args_to_replace_list.append( - xml_args_to_replace('Parameter', 'name', 'Restart Data File Name', - data_file_path)) - - dofmap_file_path = os.path.join(restart_dir, dofmap_file) - xml_args_to_replace_list.append( - xml_args_to_replace('Parameter', 'name', 'Restart DOF Map File Name', - dofmap_file_path)) - - -# Generate a input file mesh from a base file XML input file by -# searching all entries in 'list_old_value' and replacing them with 'list_new_value'. -def create_xml_file_from_base_file(self, - base_input_file, - xml_args_to_replace_list, - restart=False): - # Read base input file - new_xml = read_xml_from_file( - os.path.join(self.input_file_path, base_input_file)) - - # If restart is set to True, add restart entries to `xml_args_to_replace_list` - if restart: - read_restart_file(self, xml_args_to_replace_list) - - # Loop over different parameters - for replacement_args in xml_args_to_replace_list: - tag = replacement_args.tag - if replacement_args.tag == "Parameter": - new_attribute = "value" - elif replacement_args.tag == "Parameter name": - tag = "Parameter" - new_attribute = "name" - elif replacement_args.tag == "ParameterList": - new_attribute = "name" - else: - print( - "Error: the tag name should be either 'Parameter' " - "(to change the value of a parameter entry), " - "'Parameter name' (to change the name of a parameter), " - "or 'ParameterList' (to change the name of a parameter list).") - sys.exit() - - modify_xml_tag(new_xml, tag, replacement_args.attribute, - replacement_args.attribute_entry, new_attribute, - replacement_args.new_value, replacement_args.parent) - - # Write new input file - write_xml_to_file(new_xml, self.input_file) - - -# Delete file by name -def delete_file(self, file_name): - if os.path.exists(file_name): - try: - os.remove(file_name) - except OSError as e: - print("Error: cannot delete %s : %s" % (file_name, e.strerror)) - raise e - else: - print("File %s does not exist in %s." % (file_name, self.test_path)) - - -# Copy file to a given path -def copy_file(file_path, file_name, dest_path): - shutil.copy(os.path.join(file_path, file_name), dest_path) - - -# Get input file name, mesh type and mesh file, and output file name -def setup_input_file(self, copy_xml_file): - # Check if working directory contains an 'exodiff_file.txt' file. If not - # use the default one. - exodiff_file = "exodiff_file.txt" - if not (os.path.isfile(exodiff_file)): - self.no_exodiff_file = True - shutil.copy(os.path.join(self.main_path, exodiff_file), self.test_path) - else: - self.no_exodiff_file = False - - # Copy XML file to working directory - if copy_xml_file: - copy_file(self.input_file_path, self.input_file, self.test_path) - - # Read in input file - xml = read_xml_from_file(self.input_file) - - # Extract Exodus filenames from XML file - self.output_file = search_value_through_xml_file(xml, 'Parameter', 'name', - 'Exodus Output File')[0] - - mesh_input_type = search_value_through_xml_file(xml, 'Parameter', 'name', - 'Mesh Input Type')[0] - if mesh_input_type == "Inline": - self.mesh_file = "Inline" - else: - self.mesh_file = search_value_through_xml_file(xml, 'Parameter', - 'name', 'File Name')[0] - - # Copy mesh file to working directory if not Inline mesh type - if not self.mesh_file == "Inline": - try: - shutil.copy(os.path.join(self.mesh_file_path, self.mesh_file), - self.test_path) - except OSError as e: - print("Error: cannot copy %s : %s" % (self.mesh_file, e.strerror)) - - -# Copy files of failed regression tests to an artifact directory -def copy_file_artifact_directory(self, exodiff_step): - print("\n\nMove files to artifact directory:") - # Create sub-working directory in the artifact directory - test_dir_name = os.path.basename(os.path.normpath(self.test_path)) - test_dir_name = os.path.join("regression_failures", test_dir_name) - fail_test_path = os.path.join(parent_main_path, test_dir_name) - os.makedirs(fail_test_path, exist_ok=True) - # List of files to move (XML, exodiff_test.txt and Exodus solution files) - file_list = [self.input_file, self.output_file] - if exodiff_step: - file_list.append(self.output_file.rstrip(".exo") + "_diff.exo") - file_list.append("exodiff_file.txt") - # Copy files to artifact directory - for f in file_list: - new_f = os.path.join(fail_test_path, f) - if os.path.isfile(new_f): - os.remove(new_f) - old_f = os.path.join(self.test_path, f) - if os.path.isfile(old_f): - shutil.copy(old_f, new_f) - print(f" Copied file {old_f}.") - print("\n\n") - - -# Function to call subprocess run -def subprocess_run(command): - subprocess.run(command, shell=False, stderr=subprocess.STDOUT, check=True) - - -# Run VertexCFD code -def run_vertexcfd(self, vertexcfd_options, run_serial, run_gpu): - # Build command to run VertexCFD (same command for Tpetra and Epetra linear algebra solver) - command = ["mpirun"] - if not run_gpu: - command.append("--map-by") - if not run_serial: - command.append(os.environ.get("MAP_STRING")) - else: - command.append("ppr:1:node:pe=1") - else: - command.extend(["-np", "2"]) - command.append(self.vertexcfd_exec) - command.append("--i=" + self.input_file) - # vertexcfd_options is an empty list by default - command.extend(vertexcfd_options) - # Run VertexCFD - try: - subprocess_run(command) - except Exception: - copy_file_artifact_directory(self, exodiff_step=False) - raise - - -# Build the exodiff command to be exectuted to compare the gold -# file and the solution file. It will create a `diff` file if -# `create_diff` is set to True. -def build_exodiff_command(self, exodiff_options, create_diff): - # Parameters - file_name = "exodiff_file.txt" - test_output_file = self.output_file - gold_output_file = getattr(self, 'gold_file', self.output_file) - test_gold_path = os.path.join(self.test_path, "gold") - base_file = self.output_file.rstrip(".exo") - # Build command to run exodiff - command = [self.exodiff_exec] - command.extend(exodiff_options) - if not create_diff: - command.append("-file") - command.append(os.path.join(self.test_path, file_name)) - command.append(os.path.join(self.test_path, test_output_file)) - command.append(os.path.join(test_gold_path, gold_output_file)) - if create_diff: - command.append(os.path.join(self.test_path, base_file + "_diff.exo")) - return command - - -# Run 'exodiff' executable. 'create_diff' adds a third Exodus file -# to compute the difference between the gold file and the solution file. -def run_exodiff(self, exodiff_options): - # Run exodiff - try: - # Assemble exodiff command line - command = build_exodiff_command(self, - exodiff_options, - create_diff=False) - subprocess_run(command) - except: - # Assemble command line to create a `diff` Exodus file from - # the `exodiff` executable - command = build_exodiff_command(self, - exodiff_options, - create_diff=True) - try: - subprocess_run(command) - except: - print("Warning: failed to create solution diff file") - - # Copy files to artifact directory and re-raise error - copy_file_artifact_directory(self, exodiff_step=True) - raise - - -# Clean working directory by deleting input file, output file, -# mesh file and exodiff file if any. -def clean_working_directory(self): - print("\nClean working directory:") - # List of files to remove (always present in working directory) - file_list_remove = [self.input_file] - file_list_remove.append(self.output_file) - # Remove file in 'file_list_remove' - for file in file_list_remove: - delete_file(self, file) - - # Remove exodiff file - if self.no_exodiff_file: - exodiff_file = "exodiff_file.txt" - delete_file(self, exodiff_file) - - # Remove Exodus file (mesh file) if not Inline mesh type - if not self.mesh_file == "Inline": - delete_file(self, self.mesh_file) - - # Remove block of files with same format - block_list_remove = ['*.data'] - block_list_remove.append('*.dofmap') - - for block in block_list_remove: - file_list = glob.glob(block) - for file_name in file_list: - delete_file(self, file_name) - - -# Run test by calling all above functions -def run_test(self, - vertexcfd_options, - exodiff_options, - copy_xml_file=True, - run_serial=False, - run_gpu=False): - # Get input file, mesh file and output file - setup_input_file(self, copy_xml_file) - - # Run VertexCFD job - run_vertexcfd(self, vertexcfd_options, run_serial, run_gpu) - - # Check against gold file using exodiff. NOTE: 'command' has to be passed - # as a string here with shell=True to get it to run. - if (exodiff_options is not None): - run_exodiff(self, exodiff_options) - - -# Assert two JSON files (output file and gold file) -def assert_json_files(self, gold_file_path): - # Read in output json file - with open(self.output_file, "r") as f: - json_file = json.loads(f.read()) - - # Read in gold json file - with open(gold_file_path, "r") as g: - gold_json_file = json.loads(g.read()) - - # Assert json files - assert json_file == gold_json_file - - -# Compute convergence order -def convergence_order(error1, error2, ncell1, ncell2): - return math.log((error1 / error2), (ncell2 / ncell1)) - - -# Compare convergence order between reference and vertexcfd result -def compare_convergence(error1, - error2, - reference, - num1, - num2, - rel_tol=1e-12, - abs_tol=0.0): - for p in (1, 2): - for err1, err2, ref in zip(error1[p], error2[p], reference[p]): - assert ref == pytest.approx(convergence_order( - err1, err2, num1, num2), - rel=rel_tol, - abs=abs_tol) - - -# Compare error norms between expected value and vertexcfd result -def compare_error_norms(expected, capfd, rel_tol=1e-12, abs_tol=0.0): - - # Get captured test output for parsing - fd_out, fd_err = capfd.readouterr() - - # Let pytest re-capture the output - sys.stdout.write(fd_out) - sys.stderr.write(fd_err) - - num_expected = len(expected[1]) - - # Regular expression for the final integrated error norms header. - # This will capture the order of the norm. - header_pattern = r'^Final (?:Temporal/)?Spatial Integrated L([12]) Error Norms:' - # Regular expression for a single error norm, capturing value. - error_pattern = r'\n \w+ = (.+)' - # Combine to capture the expected number of error norms. - pattern = re.compile(header_pattern + error_pattern * num_expected, - re.MULTILINE) - - errors = [None] * 3 - for p, *err in pattern.findall(fd_out): - p = int(p) - errors[p] = [float(i) for i in err] - - # Compare L1, L2 Error Norm with expected values - for p in (1, 2): - for err, exp in zip(errors[p], expected[p]): - if exp == 0.0: - assert err == pytest.approx(exp, abs=1e-12) - else: - assert err == pytest.approx(exp, rel=rel_tol, abs=abs_tol) - - return errors - - -# Log-log plot of L1/L2 error norms with number of cells -def error_norm_plot(comp_error, ref_error, comp_ncell, ref_ncell, head_string, - coordinate): - import matplotlib.pyplot as plt - - font = { - 'size': 16, - } - - # Plot label function - def label(var): - return r'$\left\Vert {0} - \overline{{{0}}}\right\Vert^{{p=1,2}}$'.format( - var) - - # Reorganize data. - L1_comp_error = [row[1] for row in comp_error] - L2_comp_error = [row[2] for row in comp_error] - L1_ref_error = [row[1] for row in ref_error] - L2_ref_error = [row[2] for row in ref_error] - - nvar = len(L1_comp_error[0]) - - L1_comp = [] - L2_comp = [] - L1_ref = [] - L2_ref = [] - for i in range(nvar): - L1_comp.append([row[i] for row in L1_comp_error]) - L2_comp.append([row[i] for row in L2_comp_error]) - L1_ref.append([row[i] for row in L1_ref_error]) - L2_ref.append([row[i] for row in L2_ref_error]) - - # variale and file names - var_name = [0] * nvar - file_name = [0] * nvar - - var_name[0] = label(r'\rho') - var_name[1] = label(r'\rho E') - file_name[0] = 'rho.png' - file_name[1] = 'rhoE.png' - if coordinate == 'Cartesian': - var_name[2] = label(r'\rho U_x') - var_name[3] = label(r'\rho U_y') - file_name[2] = 'rhoU_x.png' - file_name[3] = 'rhoU_y.png' - if nvar == 5: - var_name[nvar - 1] = label(r'\rho U_z') - file_name[nvar - 1] = 'rhoU_z.png' - elif coordinate == 'RZ': - var_name[2] = label(r'\rho U_r') - var_name[3] = label(r'\rho U_z') - file_name[2] = 'rhoU_r.png' - file_name[3] = 'rhoU_z.png' - if nvar == 5: - var_name[nvar - 1] = label(r'\rho U_\theta') - file_name[nvar - 1] = 'rhoU_theta.png' - - # Plot L1/L2 errors for each variables - for i in range(nvar): - if (L1_ref[i][0] == 0.0 or L2_ref[i][0] == 0.0): - continue - plt.loglog(ref_ncell, L1_ref[i], 'ro-', label='L1 ref error') - plt.loglog(ref_ncell, L2_ref[i], 'r^-', label='L2 ref error') - plt.loglog(comp_ncell, L1_comp[i], 'bo-', label='L1 comp error') - plt.loglog(comp_ncell, L2_comp[i], 'b^-', label='L2 comp error') - plt.ylabel(var_name[i], fontdict=font) - plt.xlabel('N', fontdict=font) - plt.xlim([ref_ncell[0] / 2, ref_ncell[-1] * 2]) - plt.legend(loc='upper right') - plt.savefig(head_string + file_name[i]) - plt.close() From d04ea424c9458d5709d04aebbc31d36c22a00527 Mon Sep 17 00:00:00 2001 From: Zitzeronion Date: Tue, 22 Jul 2025 09:47:32 +0200 Subject: [PATCH 214/214] Minor layouting thing Added math layout to u_i^avg in the laminar flow section. --- docs/theory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/theory.md b/docs/theory.md index 8cc36ba..daf4528 100644 --- a/docs/theory.md +++ b/docs/theory.md @@ -199,7 +199,7 @@ $$ $$ ### Laminar flow -The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $u_i^{avg}$, the coordinates of the two points on the circle which can be connected by a line that passes through the circle's center. The resultant inlet velocity profiles can be calculated as follows: +The laminar flow boundary condition sets a parabolic profile for the velocity in the wall normal direction. The input parameters are the average velocity amplitude $$u_i^{avg}$$, the coordinates of the two points on the circle which can be connected by a line that passes through the circle's center. The resultant inlet velocity profiles can be calculated as follows: $$ \begin{equation}

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%0DBe-8zO?FZfd@ZOKPlF1?c5-i$} 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