Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
00f2f37
First commit of impurity test
totork Mar 11, 2026
fc31777
port unit tests from master Hermes-3 for adas
totork Mar 11, 2026
e24f53b
Add 1D C impurity test without proper success handling
totork Mar 11, 2026
209212e
Use updated Json
totork Mar 11, 2026
3fcf32b
Update impurity test
totork Mar 11, 2026
fcf029c
Add impurity ionization in test input file
totork Mar 12, 2026
122bb0a
Setup first runnable simulation with C impurities
totork Mar 12, 2026
c89b6ab
Copy json files in build folder
totork Mar 12, 2026
87a736e
Make the test copy the json files itself
totork Mar 12, 2026
687d04b
Revert "Use updated Json"
totork Mar 12, 2026
e00cf23
Tighten tolerances in 1D-neutral-fci test
totork Mar 12, 2026
4c7ef3e
Disable impurity test for now
totork Mar 12, 2026
b708d3f
Revert "Disable impurity test for now"
totork Mar 12, 2026
b0746d9
Change to previous version of boutdata in CI
totork Mar 12, 2026
fe6853c
Allow for nonvarying density
totork Mar 15, 2026
c5eaf02
Change calculation of parallel divergence of current
totork Mar 15, 2026
a45ef6e
Fix wrong atomic mass
totork Mar 15, 2026
59fd403
Delete communication in EM
totork Mar 15, 2026
f8ff908
Revert to most stable behavior
totork Mar 15, 2026
18812ed
Set the coefficients properly for laplace inversion
totork Mar 15, 2026
dc4962c
Only load velocity in anomalous_diffusion_3d when nu is set
totork Mar 15, 2026
654f45e
Dont use predescribed field for Aparsolver
totork Mar 16, 2026
58938c0
Add viscosity to Vort in fci
totork Mar 17, 2026
d68b3d4
Merge pull request #20 from totork/fci-metric-sheathtest
totork Mar 18, 2026
a86992a
Add zonal neumann to potential solve
totork Mar 20, 2026
cfbac0b
Merge branch 'fci-metric-imps' of github.com:totork/hermes-3 into fci…
totork Mar 20, 2026
808264a
Proper outer boundary
totork Mar 20, 2026
d3ea94b
Revert "Proper outer boundary"
totork Mar 20, 2026
3716093
Add option to increase phi_dissipation
totork Mar 26, 2026
c27e310
Add proper div_par operator
totork Mar 26, 2026
1abb72e
Fix phi_dissipation
totork Mar 26, 2026
55ae645
First part of time-averaging flutter
totork Mar 28, 2026
eb97ca9
Revert "First part of time-averaging flutter"
totork Apr 7, 2026
5369606
Add bracket factor to flutter terms
totork Apr 7, 2026
fd2020f
Add Apar to fci flutter
totork Apr 10, 2026
85a47cc
Fix missing bracket_factor in vorticity flutter
totork Apr 10, 2026
103b70f
Adjust communication in pressure flutter for fci
totork Apr 10, 2026
547cddd
Add flags to turn flutter off and on individually
totork Apr 16, 2026
80af919
Add grillix style flux limiter
totork Apr 16, 2026
c8b7e28
Add is_SOL
totork Apr 22, 2026
a681203
Also add sol option for time dissipation
totork Apr 22, 2026
7459d76
Add proper time handling
totork Apr 22, 2026
57b8c29
Fix amjuel reaction averaging that cause slow down
totork Apr 23, 2026
cae5de0
Merge pull request #24 from totork/fci-metric-fix-neutrals
totork Apr 23, 2026
5b63b43
Allow for non-boussinesq in relax potential
totork Apr 23, 2026
6449816
Delete more averaging
totork Apr 23, 2026
5d9038e
Merge pull request #26 from totork/fci-metric-fix-neutrals
totork Apr 23, 2026
8902c87
Add proper handling of imps in relax
totork Apr 23, 2026
32c9bdf
First commit for non-boussinesq
totork Apr 24, 2026
34da230
Dont use electrons for vorticity
totork Apr 24, 2026
d328a6a
Fix non-boussinsq
totork Apr 24, 2026
ced21d9
Fix vorticity sheath boundary handling
totork Apr 24, 2026
d99b152
enable non-boussinesq zonal neumann
totork Apr 24, 2026
a17f207
Fix zonal neumann
totork Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,32 @@ configure_file(
"${PROJECT_BINARY_DIR}/include/revision.hxx")




# Once built, copy the data and test directories
option(HERMES_COPY_EXAMPLES_TO_BUILD "Copy the examples to the build directory"
ON)

option(HERMES_COPY_JSON_DATABASE_TO_BUILD
"Copy the json database to the build directory" ON)

if(HERMES_COPY_EXAMPLES_TO_BUILD OR HERMES_COPY_JSON_DATABASE_TO_BUILD)
add_custom_target(setup-examples ALL)
else()
add_custom_target(setup-examples)
endif()


add_custom_command(TARGET setup-examples PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/examples $<TARGET_FILE_DIR:${PROJECT_NAME}>/examples)

add_custom_command(TARGET setup-examples PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/json_database $<TARGET_FILE_DIR:${PROJECT_NAME}>/json_database)



# Tests
option(HERMES_COPY_TESTS_TO_BUILD "Copy the tests to the build directory" ON)
if(HERMES_COPY_TESTS_TO_BUILD)
Expand All @@ -225,23 +251,17 @@ else()
add_custom_target(setup-test)
endif()

add_custom_target(setup-examples)

# add_custom_target(setup-examples)

# copy the data and test directories
add_custom_command(TARGET setup-examples PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/examples $<TARGET_FILE_DIR:${PROJECT_NAME}>/examples)

add_custom_command(TARGET setup-examples PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/json_database $<TARGET_FILE_DIR:${PROJECT_NAME}>/json_database)

add_custom_command(TARGET setup-test PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/tests $<TARGET_FILE_DIR:${PROJECT_NAME}>/tests)





if(HERMES_TESTS)
enable_testing()

Expand Down Expand Up @@ -298,6 +318,8 @@ if(HERMES_TESTS)
DEPENDS setup-test)
endif()
endfunction()
hermes_add_integrated_test(1D-Cimpurities-fci
REQUIRES BOUT_ENABLE_METRIC_3D)
hermes_add_integrated_test(2D-vorticity-inversion-fci
REQUIRES BOUT_ENABLE_METRIC_3D)
hermes_add_integrated_test(1D-sheathtest-recycling-fci
Expand Down
27 changes: 8 additions & 19 deletions include/amjuel_reaction.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,9 @@ protected:
BoutReal avgNe = 0.0;
BoutReal avgN1 = 0.0;
BoutReal avgTe = 0.0;
if (Ne.isFci()) {
avgNe = (4.0 * Ne[i] + Ne.ydown()[iym] + Ne.yup()[iyp]) / 6.0;
avgN1 = (4.0 * N1[i] + N1.ydown()[iym] + N1.yup()[iyp]) / 6.0;
avgTe = (4.0 * Te[i] + Te.ydown()[iym] + Te.yup()[iyp]) / 6.0;
} else {
avgNe = Ne[i];
avgN1 = N1[i];
avgTe = Te[i];
}

avgNe = Ne[i];
avgN1 = N1[i];
avgTe = Te[i];
reaction_rate[i] = avgNe * avgN1 * evaluate(rate_coefs, avgTe * Tnorm, avgNe * Nnorm) * Nnorm / FreqNorm * rate_multiplier;
}

Expand Down Expand Up @@ -179,15 +172,11 @@ protected:
BoutReal avgNe = 0.0;
BoutReal avgN1 = 0.0;
BoutReal avgTe = 0.0;
if (Ne.isFci()) {
avgNe = (4.0 * Ne[i] + Ne.ydown()[iym] + Ne.yup()[iyp]) / 6.0;
avgN1 = (4.0 * N1[i] + N1.ydown()[iym] + N1.yup()[iyp]) / 6.0;
avgTe = (4.0 * Te[i] + Te.ydown()[iym] + Te.yup()[iyp]) / 6.0;
} else {
avgNe = Ne[i];
avgN1 = N1[i];
avgTe = Te[i];
}

avgNe = Ne[i];
avgN1 = N1[i];
avgTe = Te[i];

energy_loss[i] = avgNe * avgN1 * evaluate(radiation_coefs, avgTe * Tnorm, avgNe * Nnorm) * Nnorm / (Tnorm * FreqNorm) * radiation_multiplier;
}

Expand Down
4 changes: 2 additions & 2 deletions include/electromagnetic.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ private:
BoutReal beta_em; // Normalisation coefficient mu_0 e T n / B^2

std::unique_ptr<Laplacian> aparSolver; // Laplacian solver in X-Z

bool use_normdensity;
bool const_gradient; // Set neumann boundaries by extrapolation
BoutReal apar_boundary_timescale; // Relaxation timescale
BoutReal last_time; // The last time the boundaries were updated

bool magnetic_flutter; ///< Set the magnetic flutter term?
Field3D Apar_flutter;

Field3D zeroes;
bool diagnose; ///< Output additional diagnostics?
};

Expand Down
2 changes: 1 addition & 1 deletion include/evolve_density.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private:
BoutReal AA; ///< Atomic mass e.g. proton = 1

Field3D N; ///< Species density (normalised, evolving)

bool magnetic_flutter;
bool bndry_flux; ///< Allow flows through boundaries?
bool exb_advection; ///< Include ExB advection?
bool poloidal_flows; ///< Include ExB flow in Y direction?
Expand Down
2 changes: 1 addition & 1 deletion include/evolve_momentum.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private:
BoutReal pressure_floor;
bool low_p_diffuse_perp; ///< Cross-field diffusion at low pressure?
BoutReal scale_ExB;

bool magnetic_flutter;
BoutReal hyper_z; ///< Hyper-diffusion
BoutReal hyper_nv;
std::string Vname;
Expand Down
4 changes: 3 additions & 1 deletion include/evolve_pressure.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ private:
bool thermal_conduction; ///< Include thermal conduction?
BoutReal kappa_coefficient; ///< Leading numerical coefficient in parallel heat flux calculation
BoutReal kappa_limit_alpha; ///< Flux limit if >0
bool kappa_limit_grillix;
BoutReal kappa_limit_Lpar;
bool disable_ddt;
bool p_div_v; ///< Use p*Div(v) form? False -> v * Grad(p)
BoutReal T_lowsource;
Expand All @@ -103,7 +105,7 @@ private:
Field3D source, final_source; ///< External pressure source
Field3D Sp; ///< Total pressure source
FieldGeneratorPtr source_prefactor_function;

bool magnetic_flutter;
BoutReal adapt_source;

BoutReal hyper_z; ///< Hyper-diffusion
Expand Down
3 changes: 3 additions & 0 deletions include/relax_potential.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ private:
Field3D viscosity_core;
Field3D viscosity_par;
Field3D ones;
Field3D zeroes;
Field3D is_SOL;
bool core_dissipation;
bool phi_dissipation; /// Parallel dissipation of potential
BoutReal vort_timedissipation;

Expand Down
9 changes: 7 additions & 2 deletions include/vorticity.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,25 @@ private:
BoutReal average_atomic_mass; // Weighted average atomic mass, for polarisaion current (Boussinesq approximation)
bool poloidal_flows; ///< Include poloidal ExB flow?
bool bndry_flux; ///< Allow flows through radial boundaries?

bool boussinesq;
bool collisional_friction; ///< Damping of vorticity due to collisional friction

std::unique_ptr<Laplacian> phiSolver_zonalneumann;

bool zonal_neumann;

bool sheath_boundary; ///< Set outer boundary to j=0?
bool vort_dissipation; ///< Parallel dissipation of vorticity
bool phi_dissipation; ///< Parallel dissipation of potential
BoutReal phi_diss_factor;
bool phi_sheath_dissipation; ///< Dissipation at the sheath if phi < 0
bool damp_core_vorticity; ///< Damp axisymmetric component of vorticity

bool phi_boundary_relax; ///< Relax boundary to zero-gradient
BoutReal phi_boundary_timescale; ///< Relaxation timescale [normalised]
BoutReal phi_boundary_last_update; ///< Time when last updated
bool phi_core_averagey; ///< Average phi core boundary in Y?

bool magnetic_flutter;
bool split_n0; // Split phi into n=0 and n!=0 components
LaplaceXY* laplacexy; // Laplacian solver in X-Y (n=0)
Field3D logB;
Expand Down
2 changes: 1 addition & 1 deletion src/anomalous_diffusion_3d.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void AnomalousDiffusion3D::transform(Options& state) {
? GET_NOBOUNDARY(Field3D, species["temperature"])
: 0.0;
Field3D V =
species.isSet("velocity") ? GET_NOBOUNDARY(Field3D, species["velocity"]) : 0.0;
(species.isSet("velocity") && include_nu)? GET_NOBOUNDARY(Field3D, species["velocity"]) : 0.0;

Field3D flow_xlow, flow_zlow; // Flows through cell faces

Expand Down
65 changes: 45 additions & 20 deletions src/electromagnetic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ Electromagnetic::Electromagnetic(std::string name, Options &alloptions, Solver*
.doc("Extrapolate gradient of Apar into all radial boundaries?")
.withDefault<bool>(false);

use_normdensity = options["use_normdensity"]
.doc("Use the normalized density instead of the full density in the Apar equation?")
.withDefault<bool>(false);

// Give Apar an initial value because we solve Apar by iteration
// starting from the previous solution
// Note: On restart the value is restored (if available) in restartVars
Expand Down Expand Up @@ -73,6 +77,11 @@ Electromagnetic::Electromagnetic(std::string name, Options &alloptions, Solver*
.doc("Output additional diagnostics?")
.withDefault<bool>(false);

zeroes = 0.0;
zeroes.applyBoundary("neumann");
bout::globals::mesh->communicate(zeroes);
zeroes.applyParallelBoundary("parallel_neumann_o1");

magnetic_flutter = options["magnetic_flutter"]
.doc("Set magnetic flutter terms (Apar_flutter)?")
.withDefault<bool>(false);
Expand Down Expand Up @@ -127,15 +136,21 @@ void Electromagnetic::transform(Options &state) {
const BoutReal A = get<BoutReal>(species["AA"]);

// Coefficient in front of A_||
alpha_em += floor(N, 1e-5) * (SQ(Z) / A);

if (use_normdensity) {
alpha_em += (SQ(Z) / A);
} else {
alpha_em += floor(N, 1e-5) * (SQ(Z) / A);
}

// Right hand side
Ajpar += mom * (Z / A);
}

// Invert Helmholtz equation for Apar
aparSolver->setCoefA((-beta_em) * alpha_em);

aparSolver->setCoefC(1.0);
//aparSolver->setCoefA(0.0);
if (const_gradient) {
// Set gradient boundary condition from gradient inside boundary
Field3D rhs = (-beta_em) * Ajpar;
Expand Down Expand Up @@ -173,7 +188,7 @@ void Electromagnetic::transform(Options &state) {
// Use previous value of Apar as initial guess
Apar = aparSolver->solve(rhs, Apar);
} else {
Apar = aparSolver->solve((-beta_em) * Ajpar, Apar);
Apar = aparSolver->solve((-beta_em) * Ajpar, zeroes);
}

// Save in the state
Expand All @@ -194,50 +209,60 @@ void Electromagnetic::transform(Options &state) {
const Field3D N = GET_NOBOUNDARY(Field3D, species["density"]);

Field3D nv = getNonFinal<Field3D>(species["momentum"]);
nv -= Z * N * Apar;
if (use_normdensity) {
nv -= Z * Apar;
} else {
nv -= Z * N * Apar;
}
// Note: velocity is momentum / (A * N)
Field3D v = getNonFinal<Field3D>(species["velocity"]);
v -= (Z / A) * N * Apar / floor(N, 1e-5);
// Need to update the guard cells
if (use_normdensity) {
v -= (Z / A) * Apar / floor(N, 1e-5);
} else {
v -= (Z / A) * N * Apar / floor(N, 1e-5);
}

nv.applyBoundary("neumann");
v.applyBoundary("neumann");
bout::globals::mesh->communicate(nv, v);
v.applyParallelBoundary("parallel_neumann_o1");
nv.applyParallelBoundary("parallel_neumann_o1");


set(species["momentum"], nv);
set(species["velocity"], v);
}

if (magnetic_flutter) {
// Magnetic flutter terms
Apar_flutter = Apar - DC(Apar);

if (bout::globals::mesh->isFci()){
Apar_flutter = Apar;
set(state["fields"]["Apar_flutter"], Apar);
} else {
Apar_flutter = Apar - DC(Apar);
// Ensure that guard cells are communicated
Apar.getMesh()->communicate(Apar_flutter);
Apar.getMesh()->communicate(Apar_flutter);
set(state["fields"]["Apar_flutter"], Apar_flutter);

set(state["fields"]["Apar_flutter"], Apar_flutter);

#if 0
// Create a vector A from covariant components
// (A^x, A^y, A^z)
// Note: b = e_y / (JB)
#if 0
// Create a vector A from covariant components
// (A^x, A^y, A^z)
// Note: b = e_y / (JB)
const auto* coords = Apar.getCoordinates();
Vector3D A;
A.covariant = true;
A.x = A.z = 0.0;
A.y = Apar_flutter * (coords->J * coords->Bxy);

// Perturbed magnetic field vector
// Note: Contravariant components (dB_x, dB_y, dB_z)
// Perturbed magnetic field vector
// Note: Contravariant components (dB_x, dB_y, dB_z)
Vector3D delta_B = Curl(A);

// Set components of the perturbed unit vector
// Note: Options can't (yet) contain vectors
// Set components of the perturbed unit vector
// Note: Options can't (yet) contain vectors
set(state["fields"]["deltab_flutter_x"], delta_B.x / coords->Bxy);
set(state["fields"]["deltab_flutter_z"], delta_B.z / coords->Bxy);
#endif
}
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/evolve_density.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ EvolveDensity::EvolveDensity(std::string name, Options& alloptions, Solver* solv
.doc("Perpendicular diffusion at low density")
.withDefault<bool>(false);


pressure_floor = density_floor * (1./get<BoutReal>(alloptions["units"]["eV"]));


Expand Down Expand Up @@ -131,6 +132,11 @@ EvolveDensity::EvolveDensity(std::string name, Options& alloptions, Solver* solv
.withDefault(source)
/ source_normalisation;

magnetic_flutter=
n_options["magnetic_flutter"]
.doc("Use flutter terms??")
.withDefault<bool>(true);

disable_ddt = n_options["disable_ddt"]
.withDefault<bool>(false);

Expand Down Expand Up @@ -299,12 +305,13 @@ void EvolveDensity::finally(const Options& state) {
flow_ylow = 0.0;
ddt(N) -= FV::Div_par_mod<hermes::Limiter>(N, V, fastest_wave, flow_ylow, false, dissipative);

if (state.isSection("fields") and state["fields"].isSet("Apar_flutter")) {
if (state.isSection("fields") and state["fields"].isSet("Apar_flutter") and magnetic_flutter) {
// Magnetic flutter term
const Field3D Apar_flutter = get<Field3D>(state["fields"]["Apar_flutter"]);
// Note: Using -Apar_flutter rather than reversing sign in front,
// so that upwinding is handled correctly
ddt(N) -= Div_n_g_bxGrad_f_B_XZ(N, V, -Apar_flutter);
auto* coord = N.getCoordinates();
ddt(N) -= coord->Bxy * bracket(N*V/coord->Bxy, Apar_flutter, BRACKET_ARAKAWA) * bracket_factor;
}
}

Expand Down
Loading
Loading