From 9ebe1c7f3a4b2eadbb454c660474b028a53c725b Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:03:19 -0500 Subject: [PATCH 001/127] FEAT: use bulk winds --- include/inputs.h | 1 + src/inputs.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/inputs.h b/include/inputs.h index 7c1d94fc..4f0d9df8 100644 --- a/include/inputs.h +++ b/include/inputs.h @@ -81,6 +81,7 @@ class Inputs { json get_boundary_condition_types(); std::string get_advection_neutrals_vertical(); + bool get_advection_neutrals_bulkwinds(); // ------------------------------ // Grid inputs: diff --git a/src/inputs.cpp b/src/inputs.cpp index 2e46c892..137dabd8 100644 --- a/src/inputs.cpp +++ b/src/inputs.cpp @@ -1084,6 +1084,11 @@ std::string Inputs::get_advection_neutrals_vertical() { return get_setting_str("Advection", "Neutrals", "Vertical"); } +bool Inputs::get_advection_neutrals_bulkwinds() { + return get_setting_bool("Advection", "Neutrals", "useBulkWinds"); +} + + // -------------------------------------------------------------------------- // check to see if class is ok // -------------------------------------------------------------------------- From 8769836c00285f4b054fe4143a8fd2620a60f0b2 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:04:41 -0500 Subject: [PATCH 002/127] FEAT: include mean major mass for ions --- include/ions.h | 2 ++ src/ions.cpp | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/ions.h b/include/ions.h index 17a0adea..e3f20266 100644 --- a/include/ions.h +++ b/include/ions.h @@ -57,6 +57,8 @@ class Ions { arma_cube temperature_scgc; arma_cube conduction_scgc; arma_cube electron_temperature_scgc; + arma_cube rho_scgc; + arma_cube mean_major_mass_scgc; // This is the vector that will contain all of the different species: std::vector species; diff --git a/src/ions.cpp b/src/ions.cpp index 3229fb09..492d820f 100644 --- a/src/ions.cpp +++ b/src/ions.cpp @@ -96,6 +96,10 @@ Ions::Ions(Grid grid, Planets planet) { electron_temperature_scgc.set_size(nLons, nLats, nAlts); electron_temperature_scgc.fill(200); + rho_scgc.set_size(nLons, nLats, nAlts); + mean_major_mass_scgc.set_size(nLons, nLats, nAlts); + mean_major_mass_scgc.ones(); + tmp.sources_scgc.set_size(nLons, nLats, nAlts); tmp.sources_scgc.zeros(); tmp.losses_scgc.set_size(nLons, nLats, nAlts); @@ -247,12 +251,16 @@ void Ions::fill_electrons() { report.enter(function, iFunction); species[nSpecies].density_scgc.zeros(); + rho_scgc.zeros(); - for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { species[nSpecies].density_scgc = species[nSpecies].density_scgc + species[iSpecies].density_scgc; - + rho_scgc = rho_scgc + + species[iSpecies].mass * species[iSpecies].density_scgc; + } density_scgc = species[nSpecies].density_scgc; + mean_major_mass_scgc = rho_scgc / density_scgc; report.exit(function); return; From 88a4b5e1c990e5cea88256a007aacaa7b9d7830d Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:07:01 -0500 Subject: [PATCH 003/127] FEAT: ion neutral collisions + heating --- include/neutrals.h | 15 +++++++++++++-- src/neutrals.cpp | 13 +++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/neutrals.h b/include/neutrals.h index be0c0a01..c2ed17cb 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -183,6 +183,9 @@ class Neutrals { // Source terms: + // Bulk acceleration due to collisions with ions: + std::vector acc_ion_collisions; + /// Bulk neutral thermal conduction temperature change rate (K/s) arma_cube conduction_scgc; @@ -192,6 +195,12 @@ class Neutrals { /// Bulk neutral chemical heating temperatuare change (K/s) arma_cube heating_chemical_scgc; + // Bulk neutral collisional heating with ions (K/s) + arma_cube heating_ion_collisions_scgc; + + // Total heating sources + arma_cube heating_sources_total; + /// Nuetral gas direct absorption heating efficiency (~5%) precision_t heating_efficiency; @@ -349,7 +358,7 @@ class Neutrals { \param grid The grid to define the neutrals on \param time The times within the model (dt is needed) **/ - void calc_conduction(Grid grid, Times time); + void update_temperature(Grid grid, Times time); /********************************************************************** \brief Calculate the O radiative cooling @@ -364,8 +373,10 @@ class Neutrals { /********************************************************************** \brief Add all of the neutral source terms to each of the equations \param time The times within the model (dt is needed) + \param planet Need things like rotation rate + \param grid Need things like radius **/ - void add_sources(Times time); + void add_sources(Times time, Planets planet, Grid grid); /********************************************************************** \brief Set boundary conditions for the neutrals diff --git a/src/neutrals.cpp b/src/neutrals.cpp index cf1e0bbf..6e51af60 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -135,9 +135,15 @@ Neutrals::Neutrals(Grid grid, conduction_scgc.set_size(nLons, nLats, nAlts); heating_euv_scgc.set_size(nLons, nLats, nAlts); heating_chemical_scgc.set_size(nLons, nLats, nAlts); + heating_sources_total.set_size(nLons, nLats, nAlts); + heating_sources_total.zeros(); heating_efficiency = input.get_euv_heating_eff_neutrals(); + // bulk ion_neutral collisional acceleration: + acc_ion_collisions = make_cube_vector(nLons, nLats, nAlts, 3); + + // This gets a bunch of the species-dependent characteristics: iErr = read_planet_file(planet); @@ -201,15 +207,18 @@ int Neutrals::read_planet_file(Planets planet) { } //---------------------------------------------------------------------- -// Fill With Hydrostatic Solution (all species) +// Fill With Hydrostatic Solution (all ADVECTED species) // - iEnd is NOT included (python style)! +// - only do advected, since others are probably chemistry dominated //---------------------------------------------------------------------- void Neutrals::fill_with_hydrostatic(int64_t iStart, int64_t iEnd, Grid grid) { - for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + int64_t iNeutral, iSpecies; + for (iNeutral = 0; iNeutral < nSpeciesAdvect; iNeutral++) { + iSpecies = species_to_advect[iNeutral]; // Integrate with hydrostatic equilibrium up: for (int iAlt = iStart; iAlt < iEnd; iAlt++) { species[iSpecies].density_scgc.slice(iAlt) = From 5a2c28a29a03266c31d110eb153b535d73eaba13 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:07:51 -0500 Subject: [PATCH 004/127] FEAT: neutral/ion collisional heating --- src/neutral_ion_collisions.cpp | 95 +++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/src/neutral_ion_collisions.cpp b/src/neutral_ion_collisions.cpp index bed85aae..fb98dc98 100644 --- a/src/neutral_ion_collisions.cpp +++ b/src/neutral_ion_collisions.cpp @@ -14,34 +14,101 @@ void calc_ion_collisions(Neutrals &neutrals, int64_t nY = neutrals.density_scgc.n_cols; int64_t nZ = neutrals.density_scgc.n_slices; int64_t nSpecies = neutrals.nSpecies, iSpecies; - int64_t iDir, iIon, iNeutral; + int64_t iDir, iIon, iIon_, iNeutral, iNeutral_; arma_cube rho_n(nX, nY, nZ); arma_cube rho_i(nX, nY, nZ); arma_cube rho_sum(nX, nY, nZ); + + // energy is the total energy transfered from ions to neutrals + arma_cube energy(nX, nY, nZ); + // beta is the sum of the collision frequencies * mass density of ions + arma_cube beta(nX, nY, nZ); + // velocity difference between ions and neutrals + arma_cube vDiff(nX, nY, nZ); + // momentum so we can divide by the mass density later + std::vector momentum; + momentum = make_cube_vector(nX, nY, nZ, 3); - // Calculate acceleration due to ion drag. Based on Formula 4.124b in Ionospheres text. - for (iNeutral = 0; iNeutral < neutrals.nSpeciesAdvect; iNeutral++) { - Neutrals::species_chars & advected_neutral = - neutrals.species[neutrals.species_to_advect[iNeutral]]; - rho_n = advected_neutral.mass * advected_neutral.density_scgc; + beta.zeros(); + // If we are using the bulk (horizontal, primarily) neutral winds + // then approximate some of the collisional quantities + + if (input.get_advection_neutrals_bulkwinds()) { + for (iIon = 0; iIon < ions.nSpeciesAdvect; iIon++) { + iIon_ = ions.species_to_advect[iIon]; + Ions::species_chars & advected_ion = ions.species[iIon_]; + rho_i = advected_ion.mass * advected_ion.density_scgc; + for (iNeutral = 0; iNeutral < neutrals.nSpeciesAdvect; iNeutral++) { + iNeutral_ = neutrals.species_to_advect[iNeutral]; + beta = beta + rho_i % advected_ion.nu_ion_neutral_vcgc[iNeutral_]; + } + } + + // Now use the bulk quantities for the collisions + // (beta is included in the last step) + // heat transfer between ions and neutrals: + energy = 3 * cKB / ions.mean_major_mass_scgc % + (ions.temperature_scgc - neutrals.temperature_scgc); for (iDir = 0; iDir < 3; iDir++) { - rho_sum.zeros(); + // need the velocity difference for momentum and energy eqns: + vDiff = (ions.velocity_vcgc[iDir] - neutrals.velocity_vcgc[iDir]); + // ion - neutral drag (acceleration): + neutrals.acc_ion_collisions[iDir] = + beta / neutrals.rho_scgc % vDiff; + // Frictional heating between ions and neutrals: + energy = energy + vDiff % vDiff; + } + // multiply by collision frequencies and convert + // energy change to temperature change: + neutrals.heating_ion_collisions_scgc = + beta % energy / (2 * neutrals.rho_scgc % neutrals.Cv_scgc); + } else { + energy.zeros(); + + // Calculate acceleration due to ion drag. Based on Formula 4.124b in Ionospheres text. + for (iNeutral = 0; iNeutral < neutrals.nSpeciesAdvect; iNeutral++) { + Neutrals::species_chars & advected_neutral = + neutrals.species[neutrals.species_to_advect[iNeutral]]; + rho_n = advected_neutral.mass * advected_neutral.density_scgc; + + for (iDir = 0; iDir < 3; iDir++) + momentum[iDir].zeros(); for (iIon = 0; iIon < ions.nSpeciesAdvect; iIon++) { Ions::species_chars & advected_ion = ions.species[ions.species_to_advect[iIon]]; rho_i = advected_ion.mass * advected_ion.density_scgc; - rho_sum = rho_sum + - rho_i % advected_ion.nu_ion_neutral_vcgc[iNeutral] % - (advected_ion.par_velocity_vcgc[iDir] + + beta = rho_i % advected_ion.nu_ion_neutral_vcgc[iNeutral]; + precision_t one_over_masses = 1.0 / (advected_ion.mass + advected_neutral.mass); + + // B = rho_i * Nu_in + // Acc (for each species) = sum_over_ions(B * (Vi - Vn)) / rho + // Momentum = sum(B * (Vi - Vn)) + // Energy = sum_neutrals(sum__ions(B/(Mi + Mn) * (Ti - Tn) + Mi * (Vi-Vn)^2)) + + energy = energy + 3 * cKB * one_over_masses * + (ions.temperature_scgc - neutrals.temperature_scgc); + + for (iDir = 0; iDir < 3; iDir++) { + vDiff = (advected_ion.par_velocity_vcgc[iDir] + advected_ion.perp_velocity_vcgc[iDir] - advected_neutral.velocity_vcgc[iDir]); - } // for each ion + energy = energy + (advected_ion.mass * one_over_masses) * vDiff % vDiff; + momentum[iDir] = momentum[iDir] + beta % vDiff; - advected_neutral.acc_ion_drag[iDir] = rho_sum / rho_n; - } // for each direction - } // for each neutral + } // for each ion + // + energy = energy % beta; + } // for each ion + // Divide by the mass density to get the acceleration + for (iDir = 0; iDir < 3; iDir++) + advected_neutral.acc_ion_drag[iDir] = momentum[iDir]/rho_n; + } // for each neutral + // Only one heating needed for the neutrals: + neutrals.heating_ion_collisions_scgc = + energy / (neutrals.rho_scgc % neutrals.Cv_scgc); + } // bulk neutral winds report.exit(function); return; From d64851f4e694303300398d55f28261533bf34986 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:08:43 -0500 Subject: [PATCH 005/127] FEAT: calculate bulk velocity, move conduction --- src/calc_neutral_derived.cpp | 81 +++++------------------------------- 1 file changed, 10 insertions(+), 71 deletions(-) diff --git a/src/calc_neutral_derived.cpp b/src/calc_neutral_derived.cpp index 32ca53fe..065fd02b 100644 --- a/src/calc_neutral_derived.cpp +++ b/src/calc_neutral_derived.cpp @@ -157,11 +157,18 @@ void Neutrals::assign_bulk_velocity() { static int64_t iSpecies, iDir; - // If you don't advect a species, then fill with bulk velocity: - for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) - if (!species[iSpecies].DoAdvect) + if (input.get_advection_neutrals_bulkwinds()) { + // assume every species is advected with the bunk velocity: + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) for (iDir = 0; iDir < 3; iDir++) species[iSpecies].velocity_vcgc[iDir] = velocity_vcgc[iDir]; + } else { + // If you don't advect a species, then fill with bulk velocity: + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + if (!species[iSpecies].DoAdvect) + for (iDir = 0; iDir < 3; iDir++) + species[iSpecies].velocity_vcgc[iDir] = velocity_vcgc[iDir]; + } report.exit(function); return; @@ -608,71 +615,3 @@ void Neutrals::calc_chapman(Grid grid) { report.exit(function); return; } - -// ----------------------------------------------------------------------------- -// Calculate thermal conduction -// ----------------------------------------------------------------------------- - -void Neutrals::calc_conduction(Grid grid, Times time) { - - std::string function = "Neutrals::calc_conduction"; - static int iFunction = -1; - report.enter(function, iFunction); - - precision_t dt; - int64_t iLon, iLat; - int64_t nLons = grid.get_nLons(); - int64_t nLats = grid.get_nLats(); - int64_t nAlts = grid.get_nAlts(); - int64_t nGCs = grid.get_nGCs(); - - if (nAlts == 2 * nGCs + 1) - conduction_scgc.zeros(); - - else { - - arma_cube rhocvr23d(nLons, nLats, nAlts); - arma_cube lambda3d(nLons, nLats, nAlts); - arma_cube prandtl3d(nLons, nLats, nAlts); - - rhocvr23d = rho_scgc % Cv_scgc % grid.radius2_scgc; - - // Need to make this eddy * rho * cv: - if (input.get_use_eddy_energy()) - prandtl3d = kappa_eddy_scgc % rho_scgc % Cv_scgc; - else - prandtl3d.zeros(); - - lambda3d = (kappa_scgc + prandtl3d) % grid.radius2_scgc; - - arma_vec temp1d(nAlts); - arma_vec lambda1d(nAlts); - arma_vec rhocvr21d(nAlts); - arma_vec dalt1d(nAlts); - arma_vec conduction1d(nAlts); - - for (iLon = 0; iLon < nLons; iLon++) { - for (iLat = 0; iLat < nLats; iLat++) { - - temp1d = temperature_scgc.tube(iLon, iLat); - lambda1d = lambda3d.tube(iLon, iLat); - rhocvr21d = rhocvr23d.tube(iLon, iLat); - dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); - conduction1d.zeros(); - - dt = time.get_dt(); - - conduction1d = solver_conduction(temp1d, lambda1d, rhocvr21d, dt, dalt1d); - - // We want the sources to be in terms of dT/dt, while the - // conduction actually solves for Tnew-Told, so divide by dt - - conduction_scgc.tube(iLon, iLat) = conduction1d / dt; - } // lat - } // lon - - } // if nAlts == 1 + 2*GCs - - report.exit(function); - return; -} From cd827e2713d21593c807c5a2ae688c8022c43300 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 12:09:23 -0500 Subject: [PATCH 006/127] FEAT: move and rename neutral conduction --- src/neutrals_energy.cpp | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/neutrals_energy.cpp diff --git a/src/neutrals_energy.cpp b/src/neutrals_energy.cpp new file mode 100644 index 00000000..dfce6560 --- /dev/null +++ b/src/neutrals_energy.cpp @@ -0,0 +1,74 @@ +// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) +// Full license can be found in License.md + +#include "aether.h" + +// ----------------------------------------------------------------------------- +// Calculate thermal conduction +// ----------------------------------------------------------------------------- + +void Neutrals::update_temperature(Grid grid, Times time) { + + std::string function = "Neutrals::calc_conduction"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + int64_t iLon, iLat; + int64_t nLons = grid.get_nLons(); + int64_t nLats = grid.get_nLats(); + int64_t nAlts = grid.get_nAlts(); + int64_t nGCs = grid.get_nGCs(); + + dt = time.get_dt(); + + if (nAlts == 2 * nGCs + 1) { + // Simply update the temperature if there is no conduction: + temperature_scgc = temperature_scgc + dt * heating_sources_total; + conduction_scgc.zeros(); + } else { + + arma_cube rhocvr23d(nLons, nLats, nAlts); + arma_cube lambda3d(nLons, nLats, nAlts); + arma_cube prandtl3d(nLons, nLats, nAlts); + + rhocvr23d = rho_scgc % Cv_scgc % grid.radius2_scgc; + + // Need to make this eddy * rho * cv: + if (input.get_use_eddy_energy()) + prandtl3d = kappa_eddy_scgc % rho_scgc % Cv_scgc; + else + prandtl3d.zeros(); + + lambda3d = (kappa_scgc + prandtl3d) % grid.radius2_scgc; + + arma_vec temp1d(nAlts); + arma_vec lambda1d(nAlts); + arma_vec rhocvr21d(nAlts); + arma_vec dalt1d(nAlts); + arma_vec sources1d(nAlts); + arma_vec conduction1d(nAlts); + + for (iLon = 0; iLon < nLons; iLon++) { + for (iLat = 0; iLat < nLats; iLat++) { + + temp1d = temperature_scgc.tube(iLon, iLat); + lambda1d = lambda3d.tube(iLon, iLat); + rhocvr21d = rhocvr23d.tube(iLon, iLat); + sources1d = heating_sources_total.tube(iLon, iLat); + dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); + conduction1d.zeros(); + + conduction1d = solver_conduction(temp1d, lambda1d, rhocvr21d, sources1d, dalt1d, dt, nGCs, false); + temperature_scgc.tube(iLon, iLat) = conduction1d; + + // Store the difference (as a rate), so we can output it later if we want: + conduction_scgc.tube(iLon, iLat) = (conduction1d - temp1d)/dt - sources1d; + } // lat + } // lon + + } // if nAlts == 1 + 2*GCs + + report.exit(function); + return; +} From 2572389f1bdf5ae9be9e23680bd575adb60282f4 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:23:44 -0500 Subject: [PATCH 007/127] FEAT: added coriolis and modified conduction --- include/solvers.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/include/solvers.h b/include/solvers.h index 3a20e3d4..49093b1e 100644 --- a/include/solvers.h +++ b/include/solvers.h @@ -9,18 +9,25 @@ #include using namespace arma; -arma_vec solver_conduction(arma_vec value, +arma_vec solver_conduction( + arma_vec value, arma_vec lambda, arma_vec front, + arma_vec source, + arma_vec dx, precision_t dt, - arma_vec dx); - + int64_t nGCs, + bool return_diff); arma_cube solver_chemistry(arma_cube density, arma_cube source, arma_cube loss, precision_t dt); +std::vector coriolis(std::vector velocity, + precision_t rotation_rate, + arma_cube lat_scgc); + /// Set flag values that indicate whether the previous, next, closest, /// or an interpolated value should be used. const int iPrevious_ = 1; From d82c86e01de8e0f6d6379c8dc1da881127ad1151 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:35:05 -0500 Subject: [PATCH 008/127] FEAT: modify conduction + bulk velocity stuff --- src/add_sources.cpp | 63 +++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index ba37b21d..64c2ebdd 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -10,7 +10,7 @@ // Adds all of the sources to the states. Needs time to get dt. // ----------------------------------------------------------------------------- -void Neutrals::add_sources(Times time) { +void Neutrals::add_sources(Times time, Planets planet, Grid grid) { std::string function = "add_sources"; static int iFunction = -1; @@ -18,33 +18,56 @@ void Neutrals::add_sources(Times time) { precision_t dt = time.get_dt(); - temperature_scgc = temperature_scgc + - dt * (heating_euv_scgc - + heating_chemical_scgc - + conduction_scgc - - O_cool_scgc - - NO_cool_scgc); + heating_sources_total = heating_euv_scgc + + heating_chemical_scgc + + heating_ion_collisions_scgc + - O_cool_scgc + - NO_cool_scgc; - for (int64_t iSpec = 0; iSpec < nSpeciesAdvect; iSpec++) { - species_chars & advected_neutral = species[species_to_advect[iSpec]]; + // Solve the laplace equations using the source terms, + // updating the neutral temperature: + update_temperature(grid, time); + std::vector acc_coriolis; + + // If we only consider the bulk winds in the horizontal direction: + if (input.get_advection_neutrals_bulkwinds()) { + // Calculate Coriolis: + acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); + // Add Velocity sources to bulk winds: for (int iDir = 0; iDir < 3; iDir++) { - // update velocities based on acceleration: - // reduce neutral friction until solver is added - advected_neutral.velocity_vcgc[iDir] = - advected_neutral.velocity_vcgc[iDir] + - dt * (advected_neutral.acc_neutral_friction[iDir] / 4.0 + - advected_neutral.acc_ion_drag[iDir]); - - // eddy acceleration is only in the vertical direction: - if (iDir == 2) + velocity_vcgc[iDir] = velocity_vcgc[iDir] + dt * ( + grid.cent_acc_vcgc[iDir] + + acc_coriolis[iDir] + + acc_ion_collisions[iDir]); + } + } else { + for (int64_t iSpec = 0; iSpec < nSpeciesAdvect; iSpec++) { + // Pick out the advected neutral species: + species_chars & advected_neutral = species[species_to_advect[iSpec]]; + // Calculate Coriolis: + acc_coriolis = coriolis(advected_neutral.velocity_vcgc, + planet.get_omega(), + grid.geoLat_scgc); + + for (int iDir = 0; iDir < 2; iDir++) { + // update velocities based on acceleration: + // reduce neutral friction until solver is added advected_neutral.velocity_vcgc[iDir] = + advected_neutral.velocity_vcgc[iDir] + + dt * (grid.cent_acc_vcgc[iDir] + + acc_coriolis[iDir] + + advected_neutral.acc_neutral_friction[iDir] / 4.0 + + advected_neutral.acc_ion_drag[iDir]); + // eddy acceleration is only in the vertical direction: + if (iDir == 2) advected_neutral.velocity_vcgc[iDir] = + advected_neutral.velocity_vcgc[iDir] + dt * advected_neutral.acc_eddy; + } } + calc_bulk_velocity(); } - - calc_bulk_velocity(); assign_bulk_velocity(); report.exit(function); From 4151f01f10e33907525e28a93bdd5204ebbff94b Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:36:10 -0500 Subject: [PATCH 009/127] FEAT: move conduction into add sources --- src/advance.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index c1269cd1..66ab4b9b 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -96,8 +96,6 @@ bool advance(Planets &planet, calc_aurora(gGrid, neutrals, ions); - // Calculate some neutral source terms: - neutrals.calc_conduction(gGrid, time); chemistry.calc_chemistry(neutrals, ions, time, gGrid); if (input.get_O_cooling()) @@ -110,7 +108,7 @@ bool advance(Planets &planet, calc_ion_collisions(neutrals, ions); calc_neutral_friction(neutrals); - neutrals.add_sources(time); + neutrals.add_sources(time, planet, gGrid); ions.calc_ion_temperature(neutrals, gGrid, time); ions.calc_electron_temperature(neutrals, gGrid); From 53551763529fd13696d200305cacf176e33ef3d4 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:37:07 -0500 Subject: [PATCH 010/127] FEAT: ion temperature hack at this point --- src/calc_ion_temperature.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/calc_ion_temperature.cpp b/src/calc_ion_temperature.cpp index 3ddd3627..73b4a3b8 100644 --- a/src/calc_ion_temperature.cpp +++ b/src/calc_ion_temperature.cpp @@ -45,12 +45,14 @@ void Ions::calc_ion_temperature(Neutrals neutrals, Grid grid, int64_t nLons = grid.get_nLons(); int64_t nLats = grid.get_nLats(); int64_t nAlts = grid.get_nAlts(); + int64_t nGCs = grid.get_nGCs(); arma_vec temp1d(nAlts); arma_vec lambda1d(nAlts); arma_vec front1d(nAlts); arma_vec dalt1d(nAlts); arma_vec conduction1d(nAlts); + arma_vec sources1d(nAlts); arma_cube tempT(nLons, nLats, nAlts); arma_cube tempD(nLons, nLats, nAlts); @@ -60,9 +62,11 @@ void Ions::calc_ion_temperature(Neutrals neutrals, Grid grid, for (iIon = 0; iIon < nSpecs; iIon++) species[iIon].temperature_scgc = - neutrals.temperature_scgc; + neutrals.temperature_scgc + + 1.5 * (neutrals.temperature_scgc - neutrals.temperature_scgc(2,2,2)); - temperature_scgc = neutrals.temperature_scgc; + temperature_scgc = neutrals.temperature_scgc + + 1.5 * (neutrals.temperature_scgc - neutrals.temperature_scgc(2,2,2)); report.exit(function); return; @@ -89,17 +93,16 @@ void Ions::calc_ion_temperature(Neutrals neutrals, Grid grid, // Calculate heat flux (conduction) in 1D; loop over all lat,lon // --------------------------------------------------------------------- temp1d = species[iIon].temperature_scgc.tube(iLon, iLat); - lambda1d = 25.0 * pow(cKB, 2) * pow(temp1d, 2.5) / species[iIon].mass - / species[iIon].nu_ion_ion[iIon] / 8.0; lambda1d = 25.0 * cKB * pow(temp1d, 2.5) * (cKB / species[iIon].mass) / species[iIon].nu_ion_ion[iIon] / 8.0; front1d = 2.0 / species[iIon].density_scgc.tube(iLon, iLat) / cKB / 3.0; dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); + sources1d.zeros(); conduction1d.zeros(); // reset temp variable to zero - conduction1d = solver_conduction(temp1d, lambda1d, front1d, dt, dalt1d); + conduction1d = solver_conduction(temp1d, lambda1d, front1d, sources1d, dalt1d, dt, nGCs, true); // The conduction solver gives Tnew-Told, so divide by dt conduction_scgc.tube(iLon, iLat) = conduction1d / dt; From 4b516cfacb923ddb50f3a67813abf80d09f561a7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:38:48 -0500 Subject: [PATCH 011/127] FEAT: nGCs, sources, and return updated --- src/solver_conduction.cpp | 54 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/solver_conduction.cpp b/src/solver_conduction.cpp index a1254a88..3cc7639c 100644 --- a/src/solver_conduction.cpp +++ b/src/solver_conduction.cpp @@ -6,7 +6,10 @@ #include "aether.h" // ----------------------------------------------------------------------- -// This code solves the conduction equation in 1D. +// This code solves the laplace equation in 1D: +// +// dV/dt = 1/front d/dx lambda dV/dx + source +// // Some assumptions: // - Assume that lambda and front are scaled by radius squared! // - The spacing can be non-uniform. @@ -18,8 +21,11 @@ arma_vec solver_conduction(arma_vec value, arma_vec lambda, arma_vec front, + arma_vec source, + arma_vec dx, precision_t dt, - arma_vec dx) { + int64_t nGCs, + bool return_diff) { int64_t nPts = value.n_elem; @@ -43,24 +49,24 @@ arma_vec solver_conduction(arma_vec value, int64_t i; - for (i = 2; i < nPts - 2; i++) + for (i = nGCs; i < nPts - nGCs; i++) dl(i) = di(i + 1) - di(i - 1) * r(i) * r(i) - di(i) * (1.0 - r(i) * r(i)); arma_vec a = di / du22 % r - dl / du12 % r % r; arma_vec c = di / du22 + dl / du12; arma_vec b = -1.0 / m - di / du22 % (1.0 + r) - dl / du12 % (1.0 - r % r); - arma_vec d = -1.0 * value / m; + arma_vec d = -1.0 * (value / m + source % front * dt); // Lower BCs (fixed value): - a(1) = 0.0; - b(1) = -1.0; - c(1) = 0.0; - d(1) = -1.0 * value(1); + a(nGCs-1) = 0.0; + b(nGCs-1) = -1.0; + c(nGCs-1) = 0.0; + d(nGCs-1) = -1.0 * value(nGCs-1); // Upper BCs: // This assumes a constant-gradient BC (need to change for ion and ele temps. - i = nPts - 2; + i = nPts - nGCs; a(i) = 1.0 * (r(i) * (1.0 + r(i)) * di(i) * m(i) / du22(i)); b(i) = -1.0 * (1.0 + r(i) * (1 + r(i)) * di(i) * m(i) / du22(i)); c(i) = 0.0; @@ -70,26 +76,34 @@ arma_vec solver_conduction(arma_vec value, arma_vec dp(nPts, fill::zeros); arma_vec result(nPts, fill::zeros); - cp(1) = c(1) / b(1); + cp(nGCs - 1) = c(nGCs - 1) / b(nGCs - 1); - for (i = 2; i <= nPts - 2; i++) + for (i = nGCs; i <= nPts - nGCs; i++) cp(i) = c(i) / (b(i) - cp(i - 1) * a(i)); - dp(1) = d(1) / b(1); + dp(nGCs - 1) = d(nGCs - 1) / b(nGCs - 1); - for (i = 2; i <= nPts - 2; i++) + for (i = nGCs; i <= nPts - nGCs; i++) dp(i) = (d(i) - dp(i - 1) * a(i)) / (b(i) - cp(i - 1) * a(i)); - result(nPts - 2) = dp(nPts - 2); + result(nPts - nGCs) = dp(nPts - nGCs); - for (i = nPts - 3; i > 0; i--) + for (i = nPts - nGCs - 1; i > nGCs - 1; i--) result(i) = dp(i) - cp(i) * result(i + 1); - conduction = result - value; - conduction(0) = 0.0; - conduction(1) = 0.0; - conduction(nPts - 2) = 0.0; - conduction(nPts - 1) = 0.0; + if (return_diff) { + conduction = result - value; + for (i = 0; i < nGCs; i++) { + conduction(i) = 0.0; + conduction(nPts - i - 1) = 0.0; + } + } else { + conduction = result; + for (i = 0; i < nGCs; i++) { + conduction(i) = value(i); + conduction(nPts - nGCs + i) = conduction(nPts - nGCs - 1); + } + } return conduction; } From 62680f10a0d3a0b2c895c0858d523035370dbb7f Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:40:20 -0500 Subject: [PATCH 012/127] BUG: north and east were reversed --- src/solver_coriolis.cpp | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/solver_coriolis.cpp b/src/solver_coriolis.cpp index 4744f7b5..0ab125f7 100644 --- a/src/solver_coriolis.cpp +++ b/src/solver_coriolis.cpp @@ -1,23 +1,23 @@ -// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) -// Full license can be found in License.md - -// Added by S. Annamalai - June 12, 2023 - -// This function calculates the coriolis acceleration vector for an -// object in motion in a rotating frame of reference. It uses the -// coriolis terms in equations (14), (18), (19) in -// https://drive.google.com/file/d/1Q0cMzKhdd0IoXzBl3odTge9JhBwfAyk9 - -#include "../include/aether.h" - -std::vector coriolis(std::vector velocity, - precision_t rotation_rate, - arma_cube lat_scgc) { - std::vector coriolis_vec(3); - coriolis_vec[0] = -2 * rotation_rate * velocity[1] % sin(lat_scgc); - coriolis_vec[1] = - 2 * rotation_rate * velocity[0] % sin(lat_scgc) - - 2 * rotation_rate * velocity[2] % cos(lat_scgc); - coriolis_vec[2] = 2 * rotation_rate * cos(lat_scgc) % velocity[1]; - return coriolis_vec; -} +// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) +// Full license can be found in License.md + +// Added by S. Annamalai - June 12, 2023 + +// This function calculates the coriolis acceleration vector for an +// object in motion in a rotating frame of reference. It uses the +// coriolis terms in equations (14), (18), (19) in +// https://drive.google.com/file/d/1Q0cMzKhdd0IoXzBl3odTge9JhBwfAyk9 + +#include "../include/aether.h" + +std::vector coriolis(std::vector velocity, + precision_t rotation_rate, + arma_cube lat_scgc) { + std::vector coriolis_vec(3); + coriolis_vec[0] = + 2 * rotation_rate * velocity[1] % sin(lat_scgc) - + 2 * rotation_rate * velocity[2] % cos(lat_scgc); + coriolis_vec[1] = -2 * rotation_rate * velocity[0] % sin(lat_scgc); + coriolis_vec[2] = 2 * rotation_rate * cos(lat_scgc) % velocity[1]; + return coriolis_vec; +} From 7a323859bf32b5914b57944d2ca141346a2bce33 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Mon, 27 Nov 2023 19:43:24 -0500 Subject: [PATCH 013/127] feat: bulk winds - true --- share/run/UA/inputs/defaults.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/run/UA/inputs/defaults.json b/share/run/UA/inputs/defaults.json index 8250f234..ec923bbe 100644 --- a/share/run/UA/inputs/defaults.json +++ b/share/run/UA/inputs/defaults.json @@ -23,7 +23,8 @@ "Advection" : { "Neutrals" : { "Vertical" : "rusanov", - "Horizontal" : "default"}, + "Horizontal" : "default", + "useBulkWinds" : true}, "Ions" : { "Along" : "rusanov", "Across" : "default"} }, From 97d877413c88245a85b0c35feab352a2cf761c28 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 28 Nov 2023 07:36:41 -0500 Subject: [PATCH 014/127] FEAT: horizontal advection for spherical grid --- src/solver_advection.cpp | 522 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 522 insertions(+) create mode 100644 src/solver_advection.cpp diff --git a/src/solver_advection.cpp b/src/solver_advection.cpp new file mode 100644 index 00000000..033aa6ac --- /dev/null +++ b/src/solver_advection.cpp @@ -0,0 +1,522 @@ +// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) +// Full license can be found in License.md + + +#include "aether.h" + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + +// --------------------------------------------------------- +// +// --------------------------------------------------------- + +arma_vec limiter_mc(arma_vec &left, + arma_vec &right, + int64_t nPts, + int64_t nGCs) { + + precision_t beta = 0.8; + + arma_vec s = left % right; + arma_vec combined = (left + right) * 0.5; + + left = left * beta; + right = right * beta; + arma_vec limited = left; + + for (int64_t i = 1; i < nPts + 2 * nGCs - 1; i++) { + if (s(i) < 0) { + // Sign < 0 means opposite signed left and right: + limited(i) = 0.0; + } else { + if (left(i) > 0 && right(i) > 0) { + if (right(i) < limited(i)) + limited(i) = right(i); + if (combined(i) < limited(i)) + limited(i) = combined(i); + } else { + if (right(i) > limited(i)) + limited(i) = right(i); + if (combined(i) > limited(i)) + limited(i) = combined(i); + } + } + } + return limited; +} + + +// --------------------------------------------------------- +// calc gradients at centers +// - values and x defined at centers +// --------------------------------------------------------- + +arma_vec calc_grad_1d(arma_vec &values, + arma_vec &x, + int64_t nPts, + int64_t nGCs) { + + arma_vec gradients = values * 0.0; + arma_vec gradL = values * 0.0; + arma_vec gradR = values * 0.0; + + precision_t factor1 = 0.625; + precision_t factor2 = 0.0416667; + precision_t h; + + int64_t i; + arma_vec hv = values * 0.0; + + i = nGCs - 1; + h = 2.0 / (x(i+1) - x(i)); + gradR(i) = h * (factor1 * (values(i+1) - values(i)) - + factor2 * (values(i+2) - values(i-1))); + gradL(i) = (values(i) - values(i-1)) / (x(i) - x(i-1)); + + for (i = nGCs; i < nPts + nGCs; i++) { + h = 2.0 / (x(i) - x(i-1)); + gradL(i) = h * (factor1 * (values(i) - values(i-1)) - + factor2 * (values(i+1) - values(i-2))); + h = 2.0 / (x(i+1) - x(i)); + gradR(i) = h * (factor1 * (values(i+1) - values(i)) - + factor2 * (values(i+2) - values(i-1))); + } + i = nPts + nGCs; + h = 2.0 / (x(i) - x(i-1)); + gradL(i) = h * (factor1 * (values(i) - values(i-1)) - + factor2 * (values(i+1) - values(i-2))); + gradR(i) = (values(i+1) - values(i)) / (x(i+1) - x(i)); + + gradients = limiter_mc(gradL, gradR, nPts, nGCs); + + return gradients; +} + +// --------------------------------------------------------- +// calc gradients at centers for 2d matrices +// - values and x defined at centers +// --------------------------------------------------------- + +arma_mat calc_grad(arma_mat values, + arma_mat x, + int64_t nGCs, + bool DoX) { + + arma_mat v2d, x2d; + + if (DoX) { + v2d = values; + x2d = x; + } else { + v2d = values.t(); + x2d = x.t(); + } + + int64_t nX = v2d.n_rows; + int64_t nY = v2d.n_cols; + arma_mat grad2d = v2d * 0.0; + + int64_t nPts = nX - 2 * nGCs; + arma_vec values1d(nX); + arma_vec x1d(nX); + for (int64_t j = 1; j < nY-1; j++) { + values1d = v2d.col(j); + x1d = x2d.col(j); + grad2d.col(j) = calc_grad_1d(values1d, x1d, nPts, nGCs); + } + + arma_mat gradients; + + if (DoX) { + gradients = grad2d; + } else { + gradients = grad2d.t(); + } + return gradients; +} + +// --------------------------------------------------------- +// Project gradients + values to the right face, from the left +// returned values are on the i - 1/2 edges +// (between i-1 and i cell center) +// --------------------------------------------------------- + +arma_mat project_from_left(arma_mat values, + arma_mat gradients, + arma_mat x_centers, + arma_mat x_edges, + int64_t nGCs) { + + int64_t nX = values.n_rows; + int64_t nY = values.n_cols; + + // Define at edges: + arma_mat projected(nX + 1, nY); + projected.zeros(); + + // no gradient in the 0 or iEnd cells + for (int64_t j = 0; j < nY; j++) { + for (int64_t i = 1; i < nX - 1; i++) { + projected(i + 1, j) = values(i, j) + + gradients(i, j) * (x_edges(i + 1, j) - x_centers(i, j)); + } + projected(1, j) = projected(2, j); + projected(0, j) = projected(1, j); + projected(nX, j) = projected(nX - 1, j); + } + return projected; +} + + +// --------------------------------------------------------- +// Project gradients + values to the left face, from the right +// returned values are on the i - 1 edges +// (between i-1 and i cell center) +// --------------------------------------------------------- + +arma_mat project_from_right(arma_mat values, + arma_mat gradients, + arma_mat x_centers, + arma_mat x_edges, + int64_t nGCs) { + int64_t nX = values.n_rows; + int64_t nY = values.n_cols; + + // Define at edges: + arma_mat projected(nX + 1, nY); + projected.zeros(); + + // no gradient in the 0 or iEnd cells + for (int64_t j = 0; j < nY; j++) { + for (int64_t i = 1; i < nX - 1; i++) { + projected(i, j) = values(i, j) + + gradients(i, j) * (x_edges(i, j) - x_centers(i, j)); + } + projected(0, j) = projected(1, j); + projected(nX - 1, j) = projected(nX - 2, j); + projected(nX, j) = projected(nX - 1, j); + } + return projected; +} + +// --------------------------------------------------------- +// take gradients and project to all edges +// --------------------------------------------------------- + +projection_struct project_to_edges(arma_mat &values, + arma_mat &x_centers, arma_mat &x_edges, + arma_mat &y_centers, arma_mat &y_edges, + int64_t nGCs) { + + int64_t nX = values.n_rows; + int64_t nY = values.n_cols; + + projection_struct proj; + + proj.gradLR = calc_grad(values, x_centers, nGCs, true); + proj.gradDU = calc_grad(values.t(), y_centers.t(), nGCs, true).t(); + + proj.R = project_from_left(values, proj.gradLR, + x_centers, x_edges, nGCs); + // Left side of edge from left + proj.L = project_from_right(values, proj.gradLR, + x_centers, x_edges, nGCs); + // Up side of edge from down (left) + proj.U = project_from_left(values.t(), proj.gradDU.t(), + y_centers.t(), y_edges.t(), nGCs).t(); + // Down side of edge from up (right) + proj.D = project_from_right(values.t(), proj.gradDU.t(), + y_centers.t(), y_edges.t(), nGCs).t(); + + return proj; +} + +// --------------------------------------------------------- +// +// --------------------------------------------------------- + +precision_t calc_dt(arma_mat &xWidth, + arma_mat &yWidth, + arma_mat &wsLR, + arma_mat &wsDU, + int64_t nGCs) { + + int64_t nX = xWidth.n_rows; + int64_t nY = yWidth.n_cols; + + precision_t wsX, wsY, dtX, dtY, dt; + + dt = 1e32; + + for (int64_t j = nGCs; j < nY - nGCs; j++) { + for (int64_t i = nGCs; i < nX - nGCs; i++) { + wsX = (wsLR(i+1, j) + wsLR(i, j))/2; + dtX = xWidth(i, j) / wsX; + wsY = (wsDU(i, j+1) + wsDU(i, j))/2; + dtY = yWidth(i, j) / wsY; + if (dtX < dt) dt = dtX; + if (dtY < dt) dt = dtY; + } + } + return dt; +} + +// --------------------------------------------------------- +// +// --------------------------------------------------------- + +void advect(Grid &grid, + Times &time, + Neutrals &neutrals) { + + std::string function = "advect"; + static int iFunction = -1; + report.enter(function, iFunction); + + projection_struct rhoP; + projection_struct xVelP; + projection_struct yVelP; + projection_struct tempP; + + precision_t gamma = 5.0/3.0; + precision_t dt = time.get_dt(); + + int64_t nGCs = grid.get_nGCs(); + int64_t nX = grid.get_nX(); + int64_t nY = grid.get_nY(); + int64_t nAlts = grid.get_nAlts(), iAlt; + + arma_mat x, xEdges; + arma_mat y, yEdges; + + arma_mat xVel, yVel, vel; + arma_mat velL2, velR2, velU2, velD2; + arma_mat rho, temp; + arma_mat xMomentum, yMomentum; + arma_mat grad_xMomenum, xMomentumL, xMomentumR, xMomentumD, xMomentumU; + arma_mat grad_yMomenum, yMomentumL, yMomentumR, yMomentumD, yMomentumU; + + arma_mat eq1FluxLR, eq1FluxDU; + arma_mat eq1FluxL, eq1FluxR, eq1FluxD, eq1FluxU; + + arma_mat eq2FluxLR, eq2FluxDU; + arma_mat eq2FluxL, eq2FluxR, eq2FluxD, eq2FluxU, eq2Flux; + + arma_mat eq3FluxLR, eq3FluxDU; + arma_mat eq3FluxL, eq3FluxR, eq3FluxD, eq3FluxU, eq3Flux; + + arma_mat eq4FluxLR, eq4FluxDU; + arma_mat eq4FluxL, eq4FluxR, eq4FluxD, eq4FluxU; + + arma_mat wsL, wsR, wsD, wsU, wsLR, wsDU; + + arma_mat totalE; + arma_mat grad_totalE, totaleL, totaleR, totaleD, totaleU; + arma_mat diff; + + arma_mat area, xWidth, yWidth, geometry; + + neutrals.calc_mass_density(); + + arma_mat t_to_e; + + for (iAlt = nGCs; iAlt < nAlts - nGCs; iAlt++) { + + if (report.test_verbose(3)) + std::cout << "Advection: Working with iAlt: " << iAlt << "\n"; + + xVel = neutrals.velocity_vcgc[0].slice(iAlt); + yVel = neutrals.velocity_vcgc[1].slice(iAlt); + rho = neutrals.rho_scgc.slice(iAlt); + // this is "e", or temperature expressed as an energy + t_to_e = 1.0/(gamma-1.0) * cKB / neutrals.mean_major_mass_scgc.slice(iAlt); + temp = t_to_e % neutrals.temperature_scgc.slice(iAlt); + + // ------------------------------------------------ + // Calculate derived equations (at cell centers - these will be updated) + // eq 1 = rho + // eq 2 = rho * xVel (momentum) + // eq 3 = rho * yVel (momentum) + // eq 4 = E --> rho * (temp + 0.5 * vel^2) (totalE) + + vel = sqrt(xVel % xVel + yVel % yVel); + totalE = rho % temp + 0.5 * rho % vel % vel; + xMomentum = rho % xVel; + yMomentum = rho % yVel; + + x = grid.x_Center.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + y = grid.y_Center.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + xEdges = grid.x_Left.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + yEdges = grid.y_Down.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + + rhoP = project_to_edges(rho, x, xEdges, y, yEdges, nGCs); + xVelP = project_to_edges(xVel, x, xEdges, y, yEdges, nGCs); + yVelP = project_to_edges(yVel, x, xEdges, y, yEdges, nGCs); + tempP = project_to_edges(temp, x, xEdges, y, yEdges, nGCs); + + // ------------------------------------------------ + // Calculate derived equations (at edges) + // eq 1 = rho + // eq 2 = rho * xVel (momentum) + // eq 3 = rho * yVel (momentum) + // eq 4 = E --> rho * (temp + 0.5 * vel^2) (totalE) + + report.print(3, "Advection: Deriving State Equations"); + + xMomentumL = rhoP.L % xVelP.L; + xMomentumR = rhoP.R % xVelP.R; + xMomentumD = rhoP.D % xVelP.D; + xMomentumU = rhoP.U % xVelP.U; + + yMomentumL = rhoP.L % yVelP.L; + yMomentumR = rhoP.R % yVelP.R; + yMomentumD = rhoP.D % yVelP.D; + yMomentumU = rhoP.U % yVelP.U; + + velL2 = xVelP.L % xVelP.L + yVelP.L % yVelP.L; + velR2 = xVelP.R % xVelP.R + yVelP.R % yVelP.R; + velD2 = xVelP.D % xVelP.D + yVelP.D % yVelP.D; + velU2 = xVelP.U % xVelP.U + yVelP.U % yVelP.U; + + totaleL = rhoP.L % tempP.L + 0.5 * rhoP.L % velL2; + totaleR = rhoP.R % tempP.R + 0.5 * rhoP.R % velR2; + totaleD = rhoP.D % tempP.D + 0.5 * rhoP.D % velD2; + totaleU = rhoP.U % tempP.U + 0.5 * rhoP.U % velU2; + + // ------------------------------------------------ + // Calculate fluxes of different terms at the edges: + + report.print(3, "Advection: Calculating Fluxes"); + + eq1FluxL = rhoP.L % xVelP.L; + eq1FluxR = rhoP.R % xVelP.R; + eq1FluxD = rhoP.D % yVelP.D; + eq1FluxU = rhoP.U % yVelP.U; + + eq2FluxL = rhoP.L % (xVelP.L % xVelP.L + (gamma-1) * tempP.L); + eq2FluxR = rhoP.R % (xVelP.R % xVelP.R + (gamma-1) * tempP.R); + eq2FluxD = rhoP.D % xVelP.D % yVelP.D; + eq2FluxU = rhoP.U % xVelP.U % yVelP.U; + eq2Flux = rho % xVel % yVel; + + eq3FluxR = rhoP.R % xVelP.R % yVelP.R; + eq3FluxL = rhoP.L % xVelP.L % yVelP.L; + eq3FluxD = rhoP.D % (yVelP.D % yVelP.D + (gamma-1) * tempP.D); + eq3FluxU = rhoP.U % (yVelP.U % yVelP.U + (gamma-1) * tempP.U); + eq3Flux = rho % (yVel % yVel + (gamma-1) * temp); + + eq4FluxL = rhoP.L % xVelP.L % (0.5 * velL2 + gamma * tempP.L); + eq4FluxR = rhoP.R % xVelP.R % (0.5 * velR2 + gamma * tempP.R); + eq4FluxD = rhoP.D % yVelP.D % (0.5 * velD2 + gamma * tempP.D); + eq4FluxU = rhoP.U % yVelP.U % (0.5 * velU2 + gamma * tempP.U); + + // ------------------------------------------------ + // Calculate the wave speed for the diffusive flux: + + report.print(3, "Advection: Diffusive Fluxes"); + + wsL = sqrt(velL2) + sqrt(gamma * (gamma-1) * tempP.L); + wsR = sqrt(velR2) + sqrt(gamma * (gamma-1) * tempP.R); + wsD = sqrt(velD2) + sqrt(gamma * (gamma-1) * tempP.D); + wsU = sqrt(velU2) + sqrt(gamma * (gamma-1) * tempP.U); + + wsLR = wsR; + for (int64_t i = 0; i < nX + 1; i++) { + for (int64_t j = 0; j < nY; j++) { + if (wsL(i, j) > wsLR(i, j)) wsLR(i, j) = wsL(i, j); + } + } + + wsDU = wsD; + for (int64_t i = 0; i < nX; i++) { + for (int64_t j = 0; j < nY + 1; j++) { + if (wsU(i, j) > wsDU(i, j)) wsDU(i, j) = wsU(i, j); + } + } + + // ------------------------------------------------ + // Calculate average flux at the edges: + + report.print(3, "Advection: Averaging fluxes at edges"); + + diff = rhoP.R - rhoP.L; + eq1FluxLR = (eq1FluxL + eq1FluxR) / 2 + 0.5 * wsLR % diff; + diff = rhoP.U - rhoP.D; + eq1FluxDU = (eq1FluxD + eq1FluxU) / 2 + 0.5 * wsDU % diff; + + diff = xMomentumR - xMomentumL; + eq2FluxLR = (eq2FluxL + eq2FluxR) / 2 + 0.5 * wsLR % diff; + diff = xMomentumU - xMomentumD; + eq2FluxDU = (eq2FluxD + eq2FluxU) / 2 + 0.5 * wsDU % diff; + + diff = yMomentumR - yMomentumL; + eq3FluxLR = (eq3FluxL + eq3FluxR) / 2 + 0.5 * wsLR % diff; + diff = yMomentumU - yMomentumD; + eq3FluxDU = (eq3FluxD + eq3FluxU) / 2 + 0.5 * wsDU % diff; + + diff = totaleR - totaleL; + eq4FluxLR = (eq4FluxL + eq4FluxR) / 2 + 0.5 * wsLR % diff; + diff = totaleU - totaleD; + eq4FluxDU = (eq4FluxD + eq4FluxU) / 2 + 0.5 * wsDU % diff; + + // ------------------------------------------------ + // Update values: + report.print(3, "Advection: Updating equations of state"); + + area = grid.cell_area.slice(iAlt) * grid.radius2_scgc(1,1,iAlt); + yWidth = grid.dy_Left.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + xWidth = grid.dx_Down.slice(iAlt) * grid.radius_scgc(1,1,iAlt); + + geometry = + sin(grid.geoLat_scgc.slice(iAlt)) / + cos(grid.geoLat_scgc.slice(iAlt)) / + grid.radius_scgc(1,1,iAlt); + + for (int64_t j = nGCs; j < nY - nGCs; j++) { + for (int64_t i = nGCs; i < nX - nGCs; i++) { + //if (i == nGCs) cout << "j = " << j << " " << xWidth(i,j) << "\n"; + rho(i,j) = rho(i,j) - dt * + (yWidth(i+1,j) * eq1FluxLR(i+1,j) - + yWidth(i,j) * eq1FluxLR(i,j) + + xWidth(i,j+1) * eq1FluxDU(i,j+1) - + xWidth(i,j) * eq1FluxDU(i,j)) / area(i,j); + xMomentum(i,j) = xMomentum(i,j) - dt * + ((yWidth(i+1,j) * eq2FluxLR(i+1,j) - + yWidth(i,j) * eq2FluxLR(i,j) + + xWidth(i,j+1) * eq2FluxDU(i,j+1) - + xWidth(i,j) * eq2FluxDU(i,j)) / area(i,j) - + geometry(i,j) * eq2Flux(i,j)); + yMomentum(i,j) = yMomentum(i,j) - dt * + ((yWidth(i+1,j) * eq3FluxLR(i+1,j) - + yWidth(i,j) * eq3FluxLR(i,j) + + xWidth(i,j+1) * eq3FluxDU(i,j+1) - + xWidth(i,j) * eq3FluxDU(i,j)) / area(i,j) + + geometry(i,j) * eq3Flux(i,j)); + totalE(i,j) = totalE(i,j) - dt * + (yWidth(i+1,j) * eq4FluxLR(i+1,j) - + yWidth(i,j) * eq4FluxLR(i,j) + + xWidth(i,j+1) * eq4FluxDU(i,j+1) - + xWidth(i,j) * eq4FluxDU(i,j)) / area(i,j); + } + } + + xVel = xMomentum / rho; + yVel = yMomentum / rho; + + neutrals.velocity_vcgc[0].slice(iAlt) = xVel; + neutrals.velocity_vcgc[1].slice(iAlt) = yVel; + temp = totalE / rho - 0.5 * (xVel % xVel + yVel % yVel); + + neutrals.temperature_scgc.slice(iAlt) = temp / t_to_e; + if (report.test_verbose(3) && iAlt == 8) { + std::cout << "end t : " << neutrals.temperature_scgc.slice(iAlt).min() << " " << neutrals.temperature_scgc.slice(iAlt).max() << "\n"; + std::cout << "end temp : " << temp.min() << " " << temp.max() << "\n"; + std::cout << "end xVel : " << xVel.min() << " " << xVel.max() << "\n"; + std::cout << "end yVel : " << yVel.min() << " " << yVel.max() << "\n"; + } + } + report.exit(function); + return; +} \ No newline at end of file From 23b4cfddc35d2113dc7b50027ebee5df85a50560 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 28 Nov 2023 07:37:19 -0500 Subject: [PATCH 015/127] DOC: corrected comment --- src/neutrals_bcs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neutrals_bcs.cpp b/src/neutrals_bcs.cpp index 0c7c4017..eb1aa5e5 100644 --- a/src/neutrals_bcs.cpp +++ b/src/neutrals_bcs.cpp @@ -318,7 +318,7 @@ bool Neutrals::set_horizontal_bcs(int64_t iDir, Grid grid) { } } - // iDir = 2 is left BC: + // iDir = 3 is lower BC: if (iDir == 3) { for (iX = 0; iX < nX; iX++) { for (iY = nGCs - 1; iY >= 0; iY--) { From 6ef52b8578c41a616ec31a42115f615d45f65f4e Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 28 Nov 2023 07:37:53 -0500 Subject: [PATCH 016/127] FEAT: grid terms for advection --- include/grid.h | 15 +++++++++++++++ src/init_geo_grid.cpp | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/grid.h b/include/grid.h index 44632251..fcee805e 100644 --- a/include/grid.h +++ b/include/grid.h @@ -124,6 +124,21 @@ class Grid { // Vector of dx dy of different altitudes arma_vec drefx, drefy; + /// These are switching to the LR and DU directions for generalized coords + /// They are also in radians + + arma_cube x_Center, y_Center; + arma_cube x_Left, y_Down; + + /// these are center-to-center distances in the LR (X) and DU (Y) directions: + arma_cube dx_Center, dy_Center; + /// need dx on the lower / upper edges, don't need them on the left/right + arma_cube dx_Down; + /// need dy on the left / right edges: + arma_cube dy_Left; + /// cell area (in radians^2) + arma_cube cell_area; + std::vector bfield_vcgc; arma_cube bfield_mag_scgc; std::vector bfield_unit_vcgc; diff --git a/src/init_geo_grid.cpp b/src/init_geo_grid.cpp index a904366d..6586b90c 100644 --- a/src/init_geo_grid.cpp +++ b/src/init_geo_grid.cpp @@ -650,6 +650,13 @@ void Grid::create_sphere_grid(Quadtree quadtree) { geoLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; } + arma_cube cos_lat = cos(geoLat_scgc); + cos_lat.elem( find(cos_lat < 0.00001) ).fill(0.00001); + + y_Center = geoLat_scgc; + x_Center = geoLon_scgc % cos_lat; + cell_area = dlat * dlon * cos_lat; + // --------------------------------------------- // Left Sides - edges on left side (no offset left) // --------------------------------------------- @@ -668,6 +675,13 @@ void Grid::create_sphere_grid(Quadtree quadtree) { geoLat_Left.slice(iAlt) = lat2d_left; } + arma_cube cos_lat_L = cos(geoLat_Left); + cos_lat_L.elem( find(cos_lat_L < 0.00001) ).fill(0.00001); + + x_Left = geoLon_Left % cos_lat_L; + dy_Left.set_size(nLons, nLats, nAlts); + dy_Left.fill(dlat); + // --------------------------------------------- // Down Sides - edges on down side (no offset lat) // --------------------------------------------- @@ -686,6 +700,12 @@ void Grid::create_sphere_grid(Quadtree quadtree) { geoLat_Down.slice(iAlt) = lat2d_down; } + arma_cube cos_lat_D = cos(geoLat_Down); + cos_lat_D.elem( find(cos_lat_D < 0.00001) ).fill(0.00001); + + y_Down = geoLat_Down; + dx_Down = dlon * cos_lat_D; + // --------------------------------------------- // Corner Sides - corner (no offset lat or lon) // --------------------------------------------- From c1d3d2cdad4f352bcf602c301a0914d49cde1eac Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 28 Nov 2023 07:38:25 -0500 Subject: [PATCH 017/127] FEAT: advection solver for sphere codes --- include/solvers.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/solvers.h b/include/solvers.h index 49093b1e..eaf16392 100644 --- a/include/solvers.h +++ b/include/solvers.h @@ -9,6 +9,29 @@ #include using namespace arma; +struct projection_struct { + + arma_mat gradLR; + arma_mat gradDU; + arma_mat R; + arma_mat L; + arma_mat U; + arma_mat D; + arma_mat grad_edge_LR; + arma_mat grad_edge_DU; +}; + +arma_vec limiter_mc(arma_vec &left, arma_vec &right, int64_t nPts, int64_t nGCs); +arma_vec calc_grad_1d(arma_vec &values, + arma_vec &x, + int64_t nPts, + int64_t nGCs); +arma_mat calc_grad(arma_mat values, arma_mat x, int64_t nGCs, bool DoX); + +void advect(Grid &grid, + Times &time, + Neutrals &neutrals); + arma_vec solver_conduction( arma_vec value, arma_vec lambda, From 7ae96a7ee0411cf5c083c5ad3493127079a45871 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 28 Nov 2023 07:39:07 -0500 Subject: [PATCH 018/127] FEAT: call horizontal advection --- src/advance.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/advance.cpp b/src/advance.cpp index 66ab4b9b..db36d604 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -66,6 +66,8 @@ bool advance(Planets &planet, if (input.get_nAltsGeo() > 1) neutrals.advect_vertical(gGrid, time); + advect(gGrid, time, neutrals); + if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites(); From e10b78a0472dd6fd457ce8499a226b476241c879 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:05:47 -0500 Subject: [PATCH 019/127] FEAT: added viscosity --- include/neutrals.h | 28 ++++++++- src/calc_neutral_derived.cpp | 43 ++++++++++++- src/neutrals.cpp | 8 ++- src/neutrals_momentum_viscosity.cpp | 97 +++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 src/neutrals_momentum_viscosity.cpp diff --git a/include/neutrals.h b/include/neutrals.h index c2ed17cb..8b438074 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -72,6 +72,8 @@ class Neutrals { /// concentration (density of species / total density) arma_cube concentration_scgc; + // mass concentration (mass * density of species / rho) + arma_cube mass_concentration_scgc; /// Diffusion through other neutral species: std::vector diff0; @@ -169,6 +171,9 @@ class Neutrals { /// Eddy Diffusion arma_cube kappa_eddy_scgc; + /// Viscosity + arma_cube viscosity_scgc; + /// O cooling arma_cube O_cool_scgc; @@ -186,6 +191,9 @@ class Neutrals { // Bulk acceleration due to collisions with ions: std::vector acc_ion_collisions; + // Total bulk acceleration + std::vector acc_sources_total; + /// Bulk neutral thermal conduction temperature change rate (K/s) arma_cube conduction_scgc; @@ -299,6 +307,11 @@ class Neutrals { **/ void calc_scale_height(Grid grid); + /********************************************************************** + \brief Calculate the viscosity coefficient + **/ + void calc_viscosity(); + /********************************************************************** \brief Calculate the eddy diffusion coefficient in valid pressure **/ @@ -308,7 +321,13 @@ class Neutrals { \brief Calculate the concentration for each species (species ndensity / total ndensity) **/ void calc_concentration(); - + + /********************************************************************** + \brief Calculate the density of each species from the mass concentration + for each species and rho (ndensity = con * rho / mass) + **/ + void calc_density_from_mass_concentration(); + /********************************************************************** \brief Calculate the bulk mean major mass **/ @@ -360,6 +379,13 @@ class Neutrals { **/ void update_temperature(Grid grid, Times time); + /********************************************************************** + \brief Calculate the neutral bulk horizontal viscosity + \param grid The grid to define the neutrals on + \param time The times within the model (dt is needed) + **/ + void update_horizontal_velocity(Grid grid, Times time); + /********************************************************************** \brief Calculate the O radiative cooling **/ diff --git a/src/calc_neutral_derived.cpp b/src/calc_neutral_derived.cpp index 065fd02b..6f2f1351 100644 --- a/src/calc_neutral_derived.cpp +++ b/src/calc_neutral_derived.cpp @@ -6,6 +6,23 @@ #include "aether.h" +// ---------------------------------------------------------------------- +// Calculate viscosity +// ---------------------------------------------------------------------- + +void Neutrals::calc_viscosity() { + + std::string function = "Neutrals::calc_viscosity"; + static int iFunction = -1; + report.enter(function, iFunction); + + viscosity_scgc = 0.00013 * sqrt(temperature_scgc % + mean_major_mass_scgc / cKB); + + report.exit(function); + return; +} + // ---------------------------------------------------------------------- // Calculate eddy diffusion coefficient // ---------------------------------------------------------------------- @@ -66,10 +83,34 @@ void Neutrals::calc_concentration() { static int iFunction = -1; report.enter(function, iFunction); - for (int64_t iSpecies = 0; iSpecies < nSpecies; iSpecies++) + for (int64_t iSpecies = 0; iSpecies < nSpecies; iSpecies++) { species[iSpecies].concentration_scgc = species[iSpecies].density_scgc / density_scgc; + species[iSpecies].mass_concentration_scgc = + species[iSpecies].mass * species[iSpecies].density_scgc / rho_scgc; + } + + report.exit(function); + return; +} + +// ---------------------------------------------------------------------- +// Calculate densities from mass concentration +// Assuming mass density calculation is accurate... +// ---------------------------------------------------------------------- + +void Neutrals::calc_density_from_mass_concentration() { + + std::string function = "Neutrals::calc_density_from_mass_concentration"; + static int iFunction = -1; + report.enter(function, iFunction); + for (int64_t iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + species[iSpecies].density_scgc = + rho_scgc % species[iSpecies].mass_concentration_scgc / + species[iSpecies].mass; + } + report.exit(function); return; } diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 6e51af60..6df7e8d3 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -45,6 +45,7 @@ Neutrals::species_chars Neutrals::create_species(Grid grid) { tmp.ionization_scgc.zeros(); tmp.concentration_scgc.set_size(nLons, nLats, nAlts); + tmp.mass_concentration_scgc.set_size(nLons, nLats, nAlts); tmp.density_scgc.ones(); tmp.chapman_scgc.ones(); @@ -132,6 +133,9 @@ Neutrals::Neutrals(Grid grid, kappa_eddy_scgc.set_size(nLons, nLats, nAlts); kappa_eddy_scgc.zeros(); + viscosity_scgc.set_size(nLons, nLats, nAlts); + viscosity_scgc.zeros(); + conduction_scgc.set_size(nLons, nLats, nAlts); heating_euv_scgc.set_size(nLons, nLats, nAlts); heating_chemical_scgc.set_size(nLons, nLats, nAlts); @@ -142,7 +146,9 @@ Neutrals::Neutrals(Grid grid, // bulk ion_neutral collisional acceleration: acc_ion_collisions = make_cube_vector(nLons, nLats, nAlts, 3); - + + // bulk ion_neutral collisional acceleration: + acc_sources_total = make_cube_vector(nLons, nLats, nAlts, 3); // This gets a bunch of the species-dependent characteristics: iErr = read_planet_file(planet); diff --git a/src/neutrals_momentum_viscosity.cpp b/src/neutrals_momentum_viscosity.cpp new file mode 100644 index 00000000..9782742a --- /dev/null +++ b/src/neutrals_momentum_viscosity.cpp @@ -0,0 +1,97 @@ +// Copyright 2020, the Aether Development Team (see doc/dev_team.md +// for members) Full license can be found in License.md + +#include "aether.h" + +// --------------------------------------------------------------------------- +// Calculate viscosity +// --------------------------------------------------------------------------- + +void Neutrals::update_horizontal_velocity(Grid grid, Times time) { + + std::string function = "Neutrals::update_horizontal_velocity"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + int64_t iLon, iLat, iDir; + int64_t nLons = grid.get_nLons(); + int64_t nLats = grid.get_nLats(); + int64_t nAlts = grid.get_nAlts(); + int64_t nGCs = grid.get_nGCs(); + + dt = time.get_dt(); + + if (nAlts == 2 * nGCs + 1) { + // Simply update the temperature if there is no conduction: + velocity_vcgc[0] = velocity_vcgc[0] + dt * acc_sources_total[0]; + velocity_vcgc[1] = velocity_vcgc[1] + dt * acc_sources_total[1]; + } else { + + arma_cube rhor23d(nLons, nLats, nAlts); + arma_cube lambda3d(nLons, nLats, nAlts); + //arma_cube prandtl3d(nLons, nLats, nAlts); + + rhor23d = rho_scgc % grid.radius2_scgc; + + //// Need to make this eddy * rho * cv: + //if (input.get_use_eddy_energy()) + // prandtl3d = kappa_eddy_scgc % rho_scgc % Cv_scgc; + //else + // prandtl3d.zeros(); + + lambda3d = (viscosity_scgc) % grid.radius2_scgc; + + arma_vec vel1d(nAlts); + arma_vec lambda1d(nAlts); + arma_vec rhor21d(nAlts); + arma_vec dalt1d(nAlts); + arma_vec sources1d(nAlts); + arma_vec visc1d(nAlts); + + //if (iProc == 1) { + // std::cout << "neutrals_energy heating source : "; + // display_vector(heating_sources_total.tube(11,2)); + // std::cout << " -> temp before: "; + // display_vector(temperature_scgc.tube(11,2)); + //} + + for (iDir = 0; iDir < 2; iDir++) { + for (iLon = 0; iLon < nLons; iLon++) { + for (iLat = 0; iLat < nLats; iLat++) { + + vel1d = velocity_vcgc[iDir].tube(iLon, iLat); + lambda1d = lambda3d.tube(iLon, iLat); + rhor21d = rhor23d.tube(iLon, iLat); + sources1d.zeros(); + dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); + visc1d.zeros(); + + visc1d = solver_conduction(vel1d, + lambda1d, + rhor21d, + sources1d, + dalt1d, + dt, + nGCs, + false); + velocity_vcgc[iDir].tube(iLon, iLat) = visc1d; + + // Store the difference (as a rate), so we can output it later + // if we want: + //conduction_scgc.tube(iLon, iLat) = + // (conduction1d - temp1d)/dt - sources1d; + } // lat + } // lon + } + + //if (iProc == 1) { + // std::cout << " -> temp after: "; + // display_vector(temperature_scgc.tube(11,2)); + //} + + } // if nAlts == 1 + 2*GCs + + report.exit(function); + return; +} From a5d5d66c67750d5212c091def99d9e09b7ecf678 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:06:33 -0500 Subject: [PATCH 020/127] BUG: order of cube was wrong --- src/tools.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/tools.cpp b/src/tools.cpp index 973cd234..9bf5ec10 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -18,20 +18,20 @@ void fill_corners(arma_cube &values, int64_t nGCs) { for (iGCy = 0; iGCy < nGCs; iGCy++) { // lower left: values.tube(iGCx, iGCy) = 0.5 * ( - values.tube(iGCx, nGCs) + - values.tube(nGCs, iGCy)); + values.tube(iGCx, nGCs) + + values.tube(nGCs, iGCy)); // lower right: values.tube(nXs - iGCx - 1, iGCy) = 0.5 * ( - values.tube(nXs - iGCx - 1, nGCs) + - values.tube(nXs - nGCs - 1, iGCy)); + values.tube(nXs - iGCx - 1, nGCs) + + values.tube(nXs - nGCs - 1, iGCy)); // upper left: values.tube(iGCx, nYs - iGCy - 1) = 0.5 * ( - values.tube(iGCx, nYs - nGCs - 1) + - values.tube(nGCs, nYs - iGCy - 1)); + values.tube(iGCx, nYs - nGCs - 1) + + values.tube(nGCs, nYs - iGCy - 1)); // upper right: values.tube(nXs - iGCx - 1, nYs - iGCy - 1) = 0.5 * ( - values.tube(nXs - iGCx - 1, nYs - nGCs - 1) + - values.tube(nXs - nGCs - 1, nYs - iGCy - 1)); + values.tube(nXs - iGCx - 1, nYs - nGCs - 1) + + values.tube(nXs - nGCs - 1, nYs - iGCy - 1)); } } @@ -493,13 +493,11 @@ void refvect2sph(arma_mat &u1, arma_mat &u2, arma_mat &u, arma_mat &v, //---------------------------------------------------------------------- std::vector index_to_ijk(arma_cube cube, int index) { - arma::uword x = cube.n_rows; - arma::uword y = cube.n_cols; - int altitude = index / (x * y); - int remainder = index % (x * y); - int lattitude = remainder / y; - int longitude = remainder % y; - return std::vector {lattitude, longitude, altitude}; + uvec u = ind2sub(size(cube), index); + int iLon = u(0); + int iLat = u(1); + int iAlt = u(2); + return std::vector {iLon, iLat, iAlt}; } //---------------------------------------------------------------------- @@ -520,6 +518,7 @@ bool all_finite(arma_cube cube, std::string name) { "," + std::to_string(loc[1]) + "," + std::to_string(loc[2]) + ")"; int size = locations.size(); + std::cout << "all_finite : " << cube(loc[0], loc[1], loc[2]) << "\n"; std::string error_message = std::to_string(size) + " Nonfinite values exist in " + name + From a8be372eabeffabcbda8b2ab09ef788e8c79f16c Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:07:18 -0500 Subject: [PATCH 021/127] BUG: Perturb is optional, so don't flag if not there --- src/inputs.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/inputs.cpp b/src/inputs.cpp index 137dabd8..be94710a 100644 --- a/src/inputs.cpp +++ b/src/inputs.cpp @@ -1021,7 +1021,10 @@ bool Inputs::get_use_eddy_energy() { // ----------------------------------------------------------------------- json Inputs::get_perturb_values() { - return get_setting_json("Perturb"); + json value; + if (settings.find("Perturb") != settings.end()) + value = settings.at("Perturb"); + return value; } // ----------------------------------------------------------------------- From 9058978ea4b39a888f21761795dcaca08b7dcd04 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:08:11 -0500 Subject: [PATCH 022/127] BUG: fill corners for old exchange messages --- src/exchange_messages.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/exchange_messages.cpp b/src/exchange_messages.cpp index 3791717c..efbf7b5b 100644 --- a/src/exchange_messages.cpp +++ b/src/exchange_messages.cpp @@ -612,6 +612,13 @@ bool Neutrals::exchange_old(Grid &grid) { set_horizontal_bcs(iDir, grid); } + for (int i = 0; i < nSpecies; ++i) + fill_corners(species[i].density_scgc, nG); + + fill_corners(temperature_scgc, nG); + for (int iDir = 0; iDir < 3; iDir++) + fill_corners(velocity_vcgc[iDir], nG); + // Wait for all processors to be done. MPI_Barrier(aether_comm); From 83d67983d78ba57443f32b5f3dcc7089479508fa Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:08:50 -0500 Subject: [PATCH 023/127] FEAT: add viscosity --- src/add_sources.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index 64c2ebdd..3492c90e 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -23,7 +23,7 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { + heating_ion_collisions_scgc - O_cool_scgc - NO_cool_scgc; - + // Solve the laplace equations using the source terms, // updating the neutral temperature: update_temperature(grid, time); @@ -40,7 +40,10 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { grid.cent_acc_vcgc[iDir] + acc_coriolis[iDir] + acc_ion_collisions[iDir]); + acc_sources_total[iDir].zeros(); } + // Apply Viscosity: + update_horizontal_velocity(grid, time); } else { for (int64_t iSpec = 0; iSpec < nSpeciesAdvect; iSpec++) { // Pick out the advected neutral species: From 3ee509762dcec4e2e437fff094db356ada58c35c Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:09:30 -0500 Subject: [PATCH 024/127] STY: clean up code a bit --- src/neutrals_energy.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/neutrals_energy.cpp b/src/neutrals_energy.cpp index dfce6560..a7b8d17d 100644 --- a/src/neutrals_energy.cpp +++ b/src/neutrals_energy.cpp @@ -1,11 +1,11 @@ -// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) -// Full license can be found in License.md +// Copyright 2020, the Aether Development Team (see doc/dev_team.md +// for members) Full license can be found in License.md #include "aether.h" -// ----------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // Calculate thermal conduction -// ----------------------------------------------------------------------------- +// --------------------------------------------------------------------------- void Neutrals::update_temperature(Grid grid, Times time) { @@ -59,11 +59,20 @@ void Neutrals::update_temperature(Grid grid, Times time) { dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); conduction1d.zeros(); - conduction1d = solver_conduction(temp1d, lambda1d, rhocvr21d, sources1d, dalt1d, dt, nGCs, false); + conduction1d = solver_conduction(temp1d, + lambda1d, + rhocvr21d, + sources1d, + dalt1d, + dt, + nGCs, + false); temperature_scgc.tube(iLon, iLat) = conduction1d; - // Store the difference (as a rate), so we can output it later if we want: - conduction_scgc.tube(iLon, iLat) = (conduction1d - temp1d)/dt - sources1d; + // Store the difference (as a rate), so we can output it later + // if we want: + conduction_scgc.tube(iLon, iLat) = + (conduction1d - temp1d)/dt - sources1d; } // lat } // lon From 3c69cb373bbf2224944c9b3eb4448ac9b27fb7c9 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:10:16 -0500 Subject: [PATCH 025/127] FEAT: add viscosity and exchange messages before horizontal advection --- src/advance.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/advance.cpp b/src/advance.cpp index db36d604..d2a75c35 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -46,6 +46,7 @@ bool advance(Planets &planet, neutrals.calc_pressure(); neutrals.calc_bulk_velocity(); neutrals.calc_kappa_eddy(); + neutrals.calc_viscosity(); neutrals.calc_cMax(); precision_t dtNeutral = neutrals.calc_dt(gGrid); @@ -66,12 +67,12 @@ bool advance(Planets &planet, if (input.get_nAltsGeo() > 1) neutrals.advect_vertical(gGrid, time); + neutrals.exchange_old(gGrid); advect(gGrid, time, neutrals); if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites(); - // ------------------------------------ // Calculate source terms next: From 5df1b1054ab85355f5c406c33c69b2e1946945f9 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 1 Dec 2023 07:11:09 -0500 Subject: [PATCH 026/127] FEAT: limit changes near the pole --- src/solver_advection.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/solver_advection.cpp b/src/solver_advection.cpp index 033aa6ac..7df27aeb 100644 --- a/src/solver_advection.cpp +++ b/src/solver_advection.cpp @@ -318,7 +318,10 @@ void advect(Grid &grid, arma_mat area, xWidth, yWidth, geometry; + // These are all needed by the solver: neutrals.calc_mass_density(); + neutrals.calc_mean_major_mass(); + neutrals.calc_specific_heat(); arma_mat t_to_e; @@ -507,9 +510,28 @@ void advect(Grid &grid, neutrals.velocity_vcgc[0].slice(iAlt) = xVel; neutrals.velocity_vcgc[1].slice(iAlt) = yVel; - temp = totalE / rho - 0.5 * (xVel % xVel + yVel % yVel); - - neutrals.temperature_scgc.slice(iAlt) = temp / t_to_e; + temp = (totalE / rho - 0.5 * (xVel % xVel + yVel % yVel)) / t_to_e; + precision_t fac, dm, dp; + + for (int64_t j = nGCs; j < nY - nGCs; j++) { + for (int64_t i = nGCs; i < nX - nGCs; i++) { + fac = 1.0; + if (cos(grid.geoLat_scgc(i,j,iAlt)) < 0.2) { + fac = fac * (0.2 - cos(grid.geoLat_scgc(i,j,iAlt))); + } + dm = (1.0 - fac) * neutrals.temperature_scgc(i,j,iAlt); + dp = (1.0 + fac) * neutrals.temperature_scgc(i,j,iAlt); + if (temp(i,j) < dm) temp(i,j) = dm; + if (temp(i,j) > dp) temp(i,j) = dp; + neutrals.temperature_scgc(i,j,iAlt) = temp(i,j); + + dm = (1.0 - fac) * neutrals.rho_scgc(i,j,iAlt); + dp = (1.0 + fac) * neutrals.rho_scgc(i,j,iAlt); + if (rho(i,j) < dm) rho(i,j) = dm; + if (rho(i,j) > dp) rho(i,j) = dp; + neutrals.rho_scgc(i,j,iAlt) = rho(i,j); + } + } if (report.test_verbose(3) && iAlt == 8) { std::cout << "end t : " << neutrals.temperature_scgc.slice(iAlt).min() << " " << neutrals.temperature_scgc.slice(iAlt).max() << "\n"; std::cout << "end temp : " << temp.min() << " " << temp.max() << "\n"; @@ -517,6 +539,8 @@ void advect(Grid &grid, std::cout << "end yVel : " << yVel.min() << " " << yVel.max() << "\n"; } } + neutrals.calc_density_from_mass_concentration(); + report.exit(function); return; -} \ No newline at end of file +} From df91d189a88de8582630b9940ffffa8d36a9cacd Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Sun, 10 Dec 2023 18:37:59 -0500 Subject: [PATCH 027/127] BUG: Need a real ion temperature --- src/calc_ion_temperature.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/calc_ion_temperature.cpp b/src/calc_ion_temperature.cpp index 73b4a3b8..06a09044 100644 --- a/src/calc_ion_temperature.cpp +++ b/src/calc_ion_temperature.cpp @@ -60,14 +60,11 @@ void Ions::calc_ion_temperature(Neutrals neutrals, Grid grid, // Get the time step size precision_t dt = time.get_dt(); - for (iIon = 0; iIon < nSpecs; iIon++) - species[iIon].temperature_scgc = - neutrals.temperature_scgc + - 1.5 * (neutrals.temperature_scgc - neutrals.temperature_scgc(2,2,2)); - - temperature_scgc = neutrals.temperature_scgc + - 1.5 * (neutrals.temperature_scgc - neutrals.temperature_scgc(2,2,2)); + for (iIon = 0; iIon < nSpecies; iIon++) + species[iIon].temperature_scgc = neutrals.temperature_scgc; + temperature_scgc = neutrals.temperature_scgc; + report.exit(function); return; From b9d9887df461f74827a2afb5b9b2c27ce3a66ffa Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:37:52 -0500 Subject: [PATCH 028/127] FEAT: allow no aurora --- ext/IE/EIE_Initialize.f90 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/IE/EIE_Initialize.f90 b/ext/IE/EIE_Initialize.f90 index 235f6f93..1f9ddbbf 100644 --- a/ext/IE/EIE_Initialize.f90 +++ b/ext/IE/EIE_Initialize.f90 @@ -168,18 +168,23 @@ subroutine EIE_Initialize(iOutputError) IsFound_AuroralModel = .true. endif - if (index(EIE_NameOfAuroralModel,'hpi') > 0) then + if (index(EIE_NameOfAuroralModel,'hpi') > 0) then call read_conductance_model(iError) IsFound_AuroralModel = .true. endif - if (index(EIE_NameOfAuroralModel,'pem') > 0) then + if (index(EIE_NameOfAuroralModel,'pem') > 0) then call read_conductance_model(iError) IsFound_AuroralModel = .true. endif - if (index(EIE_NameOfAuroralModel,'fta') > 0) then + if (index(EIE_NameOfAuroralModel,'fta') > 0) then + IsFound_AuroralModel = .true. + endif + if (index(EIE_NameOfAuroralModel,'zero') > 0) then + IsFound_AuroralModel = .true. + endif + if (index(EIE_NameOfAuroralModel,'none') > 0) then IsFound_AuroralModel = .true. endif - if (iDebugLevel > 4) write(*,*) "=====> Back from read conductance" if (index(EIE_NameOfEFieldModel,'amie') > 0) then From 373d2b194d8683429a1099779ac392e837ae6bce Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:38:18 -0500 Subject: [PATCH 029/127] FEAT: allow no aurora --- ext/IE/EIE_IoLibrary.f90 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/IE/EIE_IoLibrary.f90 b/ext/IE/EIE_IoLibrary.f90 index dde39c9f..6116694b 100644 --- a/ext/IE/EIE_IoLibrary.f90 +++ b/ext/IE/EIE_IoLibrary.f90 @@ -604,6 +604,14 @@ subroutine IO_GetElectronDiffuseAurora(EFluxOut, AveEOut, iError) endif + if ((index(EIE_NameOfAuroralModel,'zero') > 0) .or. & + (index(EIE_NameOfAuroralModel,'none') > 0)) then + iError = 0 + AveEOut = 3.0 + EFluxOut = 1e-9 + endif + + endif end subroutine IO_GetElectronDiffuseAurora From 9fada92c2d6d8fd5af51693378145241469aa0bb Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:39:38 -0500 Subject: [PATCH 030/127] BUG: this seems more correct --- src/calc_chemical_sources.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/calc_chemical_sources.cpp b/src/calc_chemical_sources.cpp index 1f07f13d..7498c29b 100644 --- a/src/calc_chemical_sources.cpp +++ b/src/calc_chemical_sources.cpp @@ -123,12 +123,10 @@ void Chemistry::calc_chemical_sources(Neutrals &neutrals, temp = ions.electron_temperature_scgc; else if (piecewiseTemp == "Tn") temp = neutrals.temperature_scgc; - - // Limit the reagion to where the temperautre is in the range: - change3d = change3d % (change3d > reactions[iReaction].min); - + // zero out the rate when it is outside of the temperature bounds: + change3d.elem( find(temp < reactions[iReaction].min) ).zeros(); if (reactions[iReaction].max > 0) - change3d = change3d % (change3d <= reactions[iReaction].max); + change3d.elem( find(temp > reactions[iReaction].max) ).zeros(); } // Now that the reaction rate is calculated, multiply by the @@ -145,7 +143,7 @@ void Chemistry::calc_chemical_sources(Neutrals &neutrals, } // calculate heat change - chemical_heating += change3d * reactions[iReaction].energy; + chemical_heating = chemical_heating + change3d * reactions[iReaction].energy; // Now that full loss term is calculated, we can then add this // value to the losses: From 26dc45a5abd80959667330fcbef5a1c6432bd7b3 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:40:23 -0500 Subject: [PATCH 031/127] BUG: fake ion temperature profile improved --- src/calc_ion_temperature.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/calc_ion_temperature.cpp b/src/calc_ion_temperature.cpp index 06a09044..6605b457 100644 --- a/src/calc_ion_temperature.cpp +++ b/src/calc_ion_temperature.cpp @@ -60,10 +60,11 @@ void Ions::calc_ion_temperature(Neutrals neutrals, Grid grid, // Get the time step size precision_t dt = time.get_dt(); - for (iIon = 0; iIon < nSpecies; iIon++) - species[iIon].temperature_scgc = neutrals.temperature_scgc; + temperature_scgc = 200.0 + sqrt(grid.geoAlt_scgc/1000.0 - 90.0) * 60.0; - temperature_scgc = neutrals.temperature_scgc; + for (iIon = 0; iIon < nSpecies; iIon++) + species[iIon].temperature_scgc = temperature_scgc; + //species[iIon].temperature_scgc = neutrals.temperature_scgc; report.exit(function); return; From dc0900b8e06e8c097cd3cfac307c0c38a6169197 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:41:15 -0500 Subject: [PATCH 032/127] STY: fixed weird code --- src/calc_neutral_derived.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/calc_neutral_derived.cpp b/src/calc_neutral_derived.cpp index 6f2f1351..8d24c414 100644 --- a/src/calc_neutral_derived.cpp +++ b/src/calc_neutral_derived.cpp @@ -557,20 +557,17 @@ void Neutrals::calc_chapman(Grid grid) { xp3d = grid.radius_scgc / species[iSpecies].scale_height_scgc; y3d = sqrt(0.5 * xp3d) % abs(grid.cos_sza_scgc); - iAlt = nAlts - 1; integral3d.fill(0.0); - + iAlt = nAlts - 1; integral3d.slice(iAlt) = species[iSpecies].density_scgc.slice(iAlt) % species[iSpecies].scale_height_scgc.slice(iAlt); - for (iAlt = nAlts - 1; iAlt >= 0; iAlt--) { - if (iAlt < nAlts - 1) { - integral3d.slice(iAlt) = integral3d.slice(iAlt + 1) + - species[iSpecies].density_scgc.slice(iAlt) % - grid.dalt_lower_scgc.slice(iAlt + 1); - } + for (iAlt = nAlts - 2; iAlt >= 0; iAlt--) { + integral3d.slice(iAlt) = integral3d.slice(iAlt + 1) + + species[iSpecies].density_scgc.slice(iAlt) % + grid.dalt_lower_scgc.slice(iAlt + 1); } species[iSpecies].rho_alt_int_scgc = integral3d * species[iSpecies].mass; From ee774921fcba123648e3f34d138f06e2ab7d10fc Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:41:50 -0500 Subject: [PATCH 033/127] BUG: don't interpolate if there are no values --- src/indices.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/indices.cpp b/src/indices.cpp index 97e86334..388f42c5 100644 --- a/src/indices.cpp +++ b/src/indices.cpp @@ -307,6 +307,9 @@ precision_t Indices::get_index(double time, int index) { int64_t iLow, iMid, iHigh; + if (all_indices_arrays[index].nValues <= 0) + return -1.0e32; + iLow = 0; iHigh = all_indices_arrays[index].nValues - 1; iMid = (iHigh + iLow) / 2; From 298ad31cbae59387ca40db17b70848d075280507 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:42:15 -0500 Subject: [PATCH 034/127] FEAT: add more heating terms --- src/output.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/output.cpp b/src/output.cpp index c4c49ecf..66ca3704 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -239,6 +239,22 @@ bool output(const Neutrals &neutrals, // Thermal: if (type_output == "therm") { + AllOutputContainers[iOutput].store_variable("Heating_EUV", + "Heating from EUV", + "K/s", + neutrals.heating_euv_scgc); + AllOutputContainers[iOutput].store_variable("Heating_Chemistry", + "Heating from Chemistry", + "K/s", + neutrals.heating_chemical_scgc); + AllOutputContainers[iOutput].store_variable("Heating_Collisions", + "Heating from Ion Neutral Collisions", + "K/s", + neutrals.heating_ion_collisions_scgc); + AllOutputContainers[iOutput].store_variable("Conduction", + "Conduction", + "K/s", + neutrals.conduction_scgc); AllOutputContainers[iOutput].store_variable("O Rad Cooling", "[O] Radiative Cooling", "K/s", From b24176ba4b1d0f9eb8e9c87722aaf13703497b6f Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:42:56 -0500 Subject: [PATCH 035/127] STY: need to test to see if these are the same --- src/neutrals_energy.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/neutrals_energy.cpp b/src/neutrals_energy.cpp index a7b8d17d..da6f0bf6 100644 --- a/src/neutrals_energy.cpp +++ b/src/neutrals_energy.cpp @@ -56,6 +56,11 @@ void Neutrals::update_temperature(Grid grid, Times time) { lambda1d = lambda3d.tube(iLon, iLat); rhocvr21d = rhocvr23d.tube(iLon, iLat); sources1d = heating_sources_total.tube(iLon, iLat); + + temp1d = temp1d + dt * sources1d; + sources1d.zeros(); + + dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); conduction1d.zeros(); From 34f06d0a1b1f7e288ba066ec28c5a86b162449f5 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Tue, 12 Dec 2023 10:44:29 -0500 Subject: [PATCH 036/127] FEAT: for testing --- .../run/UA/inputs/chemistry_earth_simple.csv | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 share/run/UA/inputs/chemistry_earth_simple.csv diff --git a/share/run/UA/inputs/chemistry_earth_simple.csv b/share/run/UA/inputs/chemistry_earth_simple.csv new file mode 100644 index 00000000..9f710d03 --- /dev/null +++ b/share/run/UA/inputs/chemistry_earth_simple.csv @@ -0,0 +1,45 @@ +name,loss1,loss2,loss3,->,source1,source2,source3,rate,temp dependent,Temp range,branching,heat,uncertainty,,Numerator,Denominator,Exponent,Piecewise,Min,Max,FormulaType,eol +reaction,name,name,name,goes to,name,name,name,mks,,,ratio,eV,,,K,T,dimensionless,T,K,K,"1: (numerator/temp)^exponent, 2: temp*exp(numerator/temp)",eol +R11,He+,e-,,,He,,,4.80E-18,(250/Te)^0.7,,1,0,0.1,gitm,250,Te,0.7,,,,1,eol +R23,N2+,e-,,,N_2D,N_2D,,1.39E-13,(300/Te)^0.39,,0.56,1.04,0.1,gitm,300,Te,0.39,,,,1,eol +R24,N2+,e-,,,N,N,,2.20E-13,(300/Te)^0.39,,0.44,5.77,0.1,gitm,300,Te,0.39,,,,1,eol +R26,N2+,NO,,,N2,NO+,,3.60E-16,,,1,6.33,0.1,gitm,,,,,,,,eol +R27,N2+,O,,,NO+,N_2D,,1.33E-16,(300/Ti)^0.44,Ti<=1500,1,0.7,0.1,gitm,300,Ti,0.44,Ti,0,1500,1,eol +R27,,,,,,,,6.55E-17,(Ti/1500)^0.2,Ti>1500,,,0.1,,1500,Ti,-0.2,,1500,,1,eol +R29,N2+,O,,,O+,N2,,7.00E-18,(300/Ti)^0.23,,1,1.96,0.1,gitm,300,Ti,0.23,,,,1,eol +R30,N2+,O2,,,O2+,N2,,5.10E-17,(300/Ti)^1.16,Ti<=1000,1,3.53,0.1,gitm,300,Ti,1.16,Ti,0,1000,1,eol +R30,,,,,,,,1.26E-17,(Ti/1000)^0.67,Ti>1000,,,0.1,,1000,Ti,-0.67,,1000,,1,eol +R31,NO+,e-,,,N_2D,O,,3.40E-13,(300/Te)^0.85,,1,0.38,0.1,gitm,300,Te,0.85,,,,1,eol +R32,NO+,e-,,,N,O,,6.00E-14,(300/Te)^0.85,,1,2.77,0.1,gitm,300,Te,0.85,,,,1,eol +R35,O+,N2,,,NO+,N,,1.20E-18,,,1,1.1,0.1,,,,,,,,,eol +R36,O+,NO,,,NO+,O,,7.00E-19,(300/Ti)^-0.87,,1,4.36,0.1,gitm,300,Ti,-0.87,,,,1,eol +R37,O+,O2,,,O2+,O,,1.60E-17,(300/Ti)^0.52,Ti<=900,1,1.55,0.1,gitm,300,Ti,0.52,Ti,0,900,1,eol +R37,,,,,,,,9.00E-18,(Ti/900)^0.92,Ti>900,,,0.1,,900,Ti,-0.92,,900,,1,eol +R38,O+_2D,O2,,,O2+,O,,7.00E-17,,,1,4.865,0.1,gitm,,,,,,,,eol +R39,O+_2P,O2,,,O2+,O,,1.30E-16,,,1,6.54,0.1,gitm,,,,,,,,eol +R40,O+_2P,O2,,,O+,O2,,1.30E-16,,,1,5.016,0.1,gitm,,,,,,,,eol +R41,O2+,e-,,,O,O,,1.95E-13,(300/Te)^0.70,Ti<=1200,1,50,0.1,gitm,300,Te,0.7,Ti,0,1200,1,eol +R41,,,,,,,,7.39E-14,(1200/Te)^0.56,Ti>1200,,,0.1,,1200,Te,0.56,,1200,,1,eol +R46,O2+,NO,,,NO+,O2,,4.50E-16,,,1,2.813,0.1,gitm,,,,,,,,eol +R15,N+,NO,,,N2+,O,,8.33E-17,(300/Ti)^0.24,,1,2.2,0.1,gitm (n branch),300,Ti,0.24,,,,1,eol +R16,N+,NO,,,NO+,N,,4.72E-16,(300/Ti)^0.24,,1,3.4,0.1,gitm (n branch),300,Ti,0.24,,,,1,eol +R17,N+,O,,,O+,N,,2.20E-18,,,1,0.93,0.1,gitm (n branch),,,,,,,,eol +R18,N+,O2,,,NO+,O_1D,,5.50E-16,(300/Ti)^0.45,Ti<=1000,0.36,4.71,0.1,gitm (n branch),300,Ti,0.45,Ti,0,1000,1,eol +R18,,,,,,,,9.50E-16,,Ti>1000,,,0.1,,,,,,1000,,,eol +R19,N+,O2,,,NO+,O_3P,,5.50E-16,(300/Ti)^0.45,Ti<=1000,0.09,6.67,0.1,gitm (n branch),300,Ti,0.45,Ti,0,1000,1,eol +R19,,,,,,,,9.50E-16,,Ti>1000,,,0.1,,,,,,1000,,,eol +R20,N+,O2,,,O+_4S,NO,,5.50E-16,(300/Ti)^0.45,Ti<=1000,0.05,2.31,0.1,gitm (n branch),300,Ti,0.45,Ti,0,1000,1,eol +R20,,,,,,,,9.50E-16,,Ti>1000,,,0.1,,,,,,1000,,,eol +R21,N+,O2,,,O2+,N_2D,,5.50E-16,(300/Ti)^0.45,Ti<=1000,0.15,0.1,0.1,gitm (n branch),300,Ti,0.45,Ti,0,1000,1,eol +R21,,,,,,,,9.50E-16,,Ti>1000,,,0.1,,,,,,1000,,,eol +R22,N+,O2,,,O2+,N_4S,,5.50E-16,(300/Ti)^0.45,Ti<=1000,0.35,2.5,0.1,gitm (n branch),300,Ti,0.45,Ti,0,1000,1,eol +R22,,,,,,,,9.50E-16,,Ti>1000,,,0.1,,,,,,1000,,,eol +R2,He+,N2,,,He,N,N+,7.80E-16,,,1,0.28,0.1,gitm,,,,,,,,eol +R3,He+,N2,,,He,N2+,,5.20E-16,,,1,0,0.1,no heating?,,,,,,,,eol +R4,He+,NO,,,He,N+,O,1.35E-15,,,1,0,0.1,,,,,,,,,eol +R5,He+,NO,,,He,O+,N,1.00E-16,,,1,0,0.1,,,,,,,,,eol +R6,He+,O2,,,He,O+_2D,O,2.37E-16,,,1,0,0.1,,,,,,,,,eol +R7,He+,O2,,,He,O+_2P,O,6.04E-17,,,1,0,0.1,,,,,,,,,eol +R8,He+,O2,,,He,O+_4S,O,2.39E-17,,,1,0,0.1,,,,,,,,,eol +R9,He+,O2,,,He,O+,O_1D,4.60E-17,,,1,0,0.1,,,,,,,,,eol +R10,He+,O2,,,He,O2+,,9.20E-18,,,1,0,0.1,,,,,,,,,eol From 689ed57c313d0a95413ad6653550a710cd97aa2e Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:23:42 -0500 Subject: [PATCH 037/127] FEAT: flags for centripedal and coriolis --- include/inputs.h | 3 ++- src/inputs.cpp | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/inputs.h b/include/inputs.h index 4f0d9df8..6ff0948b 100644 --- a/include/inputs.h +++ b/include/inputs.h @@ -72,7 +72,8 @@ class Inputs { bool get_NO_cooling(); bool get_O_cooling(); - bool get_cent_acc(); + bool get_use_centripetal(); + bool get_use_coriolis(); std::string get_student_name(); bool get_is_student(); diff --git a/src/inputs.cpp b/src/inputs.cpp index be94710a..5dedd054 100644 --- a/src/inputs.cpp +++ b/src/inputs.cpp @@ -694,8 +694,16 @@ bool Inputs::get_O_cooling() { // Return centripetal acceleration // ----------------------------------------------------------------------- -bool Inputs::get_cent_acc() { - return get_setting_bool("Sources", "Grid", "Cent_acc"); +bool Inputs::get_use_centripetal() { + return get_setting_bool("Sources", "Grid", "Centripetal"); +} + +// ----------------------------------------------------------------------- +// Return coriolis acceleration +// ----------------------------------------------------------------------- + +bool Inputs::get_use_coriolis() { + return get_setting_bool("Sources", "Grid", "Coriolis"); } // ----------------------------------------------------------------------- From ff33c70e6f357eb15ffc8c1d7223318729dd1ed6 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:24:17 -0500 Subject: [PATCH 038/127] FEAT: flags for centripedal and coriolis --- share/run/UA/inputs/defaults.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/run/UA/inputs/defaults.json b/share/run/UA/inputs/defaults.json index ec923bbe..8bf9e0b9 100644 --- a/share/run/UA/inputs/defaults.json +++ b/share/run/UA/inputs/defaults.json @@ -92,7 +92,8 @@ "Sources" : { "Grid" : { - "Cent_acc" : true }, + "Centripetal" : true, + "Coriolis" : true }, "Neutrals" : { "NO_cool" : false, "O_cool": false } }, From 4faaa6d8f2a06df8c0f6a3dcd473c8eaba7d2158 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:26:50 -0500 Subject: [PATCH 039/127] FEAT: flag for coriolis added --- src/add_sources.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index 3492c90e..37916aff 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -29,11 +29,13 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { update_temperature(grid, time); std::vector acc_coriolis; + acc_coriolis = make_cube_vector(grid.get_nX(), grid.get_nY(), grid.get_nZ(), 3); // If we only consider the bulk winds in the horizontal direction: if (input.get_advection_neutrals_bulkwinds()) { // Calculate Coriolis: - acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); + if (input.get_use_coriolis()) + acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); // Add Velocity sources to bulk winds: for (int iDir = 0; iDir < 3; iDir++) { velocity_vcgc[iDir] = velocity_vcgc[iDir] + dt * ( @@ -49,9 +51,10 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { // Pick out the advected neutral species: species_chars & advected_neutral = species[species_to_advect[iSpec]]; // Calculate Coriolis: - acc_coriolis = coriolis(advected_neutral.velocity_vcgc, - planet.get_omega(), - grid.geoLat_scgc); + if (input.get_use_coriolis()) + acc_coriolis = coriolis(advected_neutral.velocity_vcgc, + planet.get_omega(), + grid.geoLat_scgc); for (int iDir = 0; iDir < 2; iDir++) { // update velocities based on acceleration: From c5dc02a2729c2b8f53c6baa2d0e2fd1835fad53d Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:27:43 -0500 Subject: [PATCH 040/127] BUG: reverse horizontal and vertical solver order --- src/advance.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index d2a75c35..a125fc94 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -64,12 +64,12 @@ bool advance(Planets &planet, if (didWork) didWork = neutrals.set_bcs(gGrid, time, indices); - if (input.get_nAltsGeo() > 1) - neutrals.advect_vertical(gGrid, time); - neutrals.exchange_old(gGrid); advect(gGrid, time, neutrals); + if (input.get_nAltsGeo() > 1) + neutrals.advect_vertical(gGrid, time); + if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites(); From 061d9ecdc67e33a5ae0d1a7de47e25e43a5a281f Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:28:23 -0500 Subject: [PATCH 041/127] FEAT: use real gamma and not 5/3 --- src/solver_advection.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/solver_advection.cpp b/src/solver_advection.cpp index 7df27aeb..3f89e982 100644 --- a/src/solver_advection.cpp +++ b/src/solver_advection.cpp @@ -279,6 +279,7 @@ void advect(Grid &grid, projection_struct xVelP; projection_struct yVelP; projection_struct tempP; + projection_struct gammaP; precision_t gamma = 5.0/3.0; precision_t dt = time.get_dt(); @@ -318,6 +319,8 @@ void advect(Grid &grid, arma_mat area, xWidth, yWidth, geometry; + arma_mat gamma2d; + // These are all needed by the solver: neutrals.calc_mass_density(); neutrals.calc_mean_major_mass(); @@ -334,7 +337,8 @@ void advect(Grid &grid, yVel = neutrals.velocity_vcgc[1].slice(iAlt); rho = neutrals.rho_scgc.slice(iAlt); // this is "e", or temperature expressed as an energy - t_to_e = 1.0/(gamma-1.0) * cKB / neutrals.mean_major_mass_scgc.slice(iAlt); + gamma2d = neutrals.gamma_scgc.slice(iAlt); + t_to_e = 1.0 / (gamma2d - 1.0) * cKB / neutrals.mean_major_mass_scgc.slice(iAlt); temp = t_to_e % neutrals.temperature_scgc.slice(iAlt); // ------------------------------------------------ @@ -358,6 +362,7 @@ void advect(Grid &grid, xVelP = project_to_edges(xVel, x, xEdges, y, yEdges, nGCs); yVelP = project_to_edges(yVel, x, xEdges, y, yEdges, nGCs); tempP = project_to_edges(temp, x, xEdges, y, yEdges, nGCs); + gammaP = project_to_edges(gamma2d, x, xEdges, y, yEdges, nGCs); // ------------------------------------------------ // Calculate derived equations (at edges) @@ -398,32 +403,32 @@ void advect(Grid &grid, eq1FluxD = rhoP.D % yVelP.D; eq1FluxU = rhoP.U % yVelP.U; - eq2FluxL = rhoP.L % (xVelP.L % xVelP.L + (gamma-1) * tempP.L); - eq2FluxR = rhoP.R % (xVelP.R % xVelP.R + (gamma-1) * tempP.R); + eq2FluxL = rhoP.L % (xVelP.L % xVelP.L + (gammaP.L - 1) % tempP.L); + eq2FluxR = rhoP.R % (xVelP.R % xVelP.R + (gammaP.R - 1) % tempP.R); eq2FluxD = rhoP.D % xVelP.D % yVelP.D; eq2FluxU = rhoP.U % xVelP.U % yVelP.U; eq2Flux = rho % xVel % yVel; eq3FluxR = rhoP.R % xVelP.R % yVelP.R; eq3FluxL = rhoP.L % xVelP.L % yVelP.L; - eq3FluxD = rhoP.D % (yVelP.D % yVelP.D + (gamma-1) * tempP.D); - eq3FluxU = rhoP.U % (yVelP.U % yVelP.U + (gamma-1) * tempP.U); - eq3Flux = rho % (yVel % yVel + (gamma-1) * temp); + eq3FluxD = rhoP.D % (yVelP.D % yVelP.D + (gammaP.D - 1) % tempP.D); + eq3FluxU = rhoP.U % (yVelP.U % yVelP.U + (gammaP.U - 1) % tempP.U); + eq3Flux = rho % (yVel % yVel + (gamma2d - 1) % temp); - eq4FluxL = rhoP.L % xVelP.L % (0.5 * velL2 + gamma * tempP.L); - eq4FluxR = rhoP.R % xVelP.R % (0.5 * velR2 + gamma * tempP.R); - eq4FluxD = rhoP.D % yVelP.D % (0.5 * velD2 + gamma * tempP.D); - eq4FluxU = rhoP.U % yVelP.U % (0.5 * velU2 + gamma * tempP.U); + eq4FluxL = rhoP.L % xVelP.L % (0.5 * velL2 + gammaP.L % tempP.L); + eq4FluxR = rhoP.R % xVelP.R % (0.5 * velR2 + gammaP.R % tempP.R); + eq4FluxD = rhoP.D % yVelP.D % (0.5 * velD2 + gammaP.D % tempP.D); + eq4FluxU = rhoP.U % yVelP.U % (0.5 * velU2 + gammaP.U % tempP.U); // ------------------------------------------------ // Calculate the wave speed for the diffusive flux: report.print(3, "Advection: Diffusive Fluxes"); - wsL = sqrt(velL2) + sqrt(gamma * (gamma-1) * tempP.L); - wsR = sqrt(velR2) + sqrt(gamma * (gamma-1) * tempP.R); - wsD = sqrt(velD2) + sqrt(gamma * (gamma-1) * tempP.D); - wsU = sqrt(velU2) + sqrt(gamma * (gamma-1) * tempP.U); + wsL = sqrt(velL2) + sqrt(gammaP.L % (gammaP.L - 1) % tempP.L); + wsR = sqrt(velR2) + sqrt(gammaP.R % (gammaP.R - 1) % tempP.R); + wsD = sqrt(velD2) + sqrt(gammaP.D % (gammaP.D - 1) % tempP.D); + wsU = sqrt(velU2) + sqrt(gammaP.U % (gammaP.U - 1) % tempP.U); wsLR = wsR; for (int64_t i = 0; i < nX + 1; i++) { From 4639e9ec97d5a6d12ccff70d8c876353f934c394 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:32:56 -0500 Subject: [PATCH 042/127] BUG: only bulk vertical winds --- src/solver_vertical_rusanov.cpp | 41 ++++++++++----------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/src/solver_vertical_rusanov.cpp b/src/solver_vertical_rusanov.cpp index 0b9258aa..d517e604 100644 --- a/src/solver_vertical_rusanov.cpp +++ b/src/solver_vertical_rusanov.cpp @@ -277,7 +277,7 @@ void Neutrals::solver_vertical_rusanov(Grid grid, species[iSpecies].velocity_vcgc[2] - dt * (species[iSpecies].velocity_vcgc[2] % gradVertVel_s[iSpecies] - v2or - + 0.1 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + + 0.05 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + gradTemp * cKB / mass + abs(grid.gravity_vcgc[2]))) + dt * diffVertVel_s[iSpecies]; @@ -294,28 +294,9 @@ void Neutrals::solver_vertical_rusanov(Grid grid, + dt * diffTemp; for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) - if (species[iSpecies].DoAdvect) { - - for (iX = nGCs; iX < nXs - nGCs; iX++) - for (iY = nGCs; iY < nYs - nGCs; iY++) - for (iZ = nGCs; iZ < nZs - nGCs; iZ++) { - if (abs(species[iSpecies].newVelocity_vcgc[2](iX, iY, iZ)) > 100.0) { - if (species[iSpecies].newVelocity_vcgc[2](iX, iY, iZ) > 100.0) - species[iSpecies].newVelocity_vcgc[2](iX, iY, iZ) = 100.0; - else - species[iSpecies].newVelocity_vcgc[2](iX, iY, iZ) = -100.0; - } - - if (newTemperature_scgc(iX, iY, iZ) < 100.0 || - std::isnan(newTemperature_scgc(iX, iY, iZ))) { - std::cout << "low temp found : " - << iX << " " - << iY << " " - << iZ << " " - << newTemperature_scgc(iX, iY, iZ) << " "; - } - } - } + if (species[iSpecies].DoAdvect) + species[iSpecies].newVelocity_vcgc[2].clamp(-100,100); + newTemperature_scgc.clamp(10, 1e32); for (iX = nGCs; iX < nXs - nGCs; iX++) for (iY = nGCs; iY < nYs - nGCs; iY++) @@ -336,13 +317,15 @@ void Neutrals::solver_vertical_rusanov(Grid grid, } } - // If you don't advect a species, then fill with hydrostatic: - for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) - if (!species[iSpecies].DoAdvect) - fill_with_hydrostatic(iSpecies, nGCs, nZs, grid); - calc_mass_density(); - calc_bulk_velocity(); + // Calculate bulk vertical winds: + velocity_vcgc[2].zeros(); + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + if (species[iSpecies].DoAdvect) { + velocity_vcgc[2] = velocity_vcgc[2] + + species[iSpecies].mass * species[iSpecies].density_scgc % + species[iSpecies].velocity_vcgc[2] / rho_scgc; + } report.exit(function); return; From fe48f6f12cd5a747a07f1e1e659ff5af09eaa4a7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 07:34:11 -0500 Subject: [PATCH 043/127] FEAT: input files for acheron --- share/run/UA/inputs/acheron.in | 55 +++++++++++++++++++++ share/run/UA/inputs/aether.json.acheron | 58 +++++++++++++++++++++++ share/run/UA/inputs/aurora_acheron.csv | 1 + share/run/UA/inputs/chemistry_acheron.csv | 3 ++ 4 files changed, 117 insertions(+) create mode 100755 share/run/UA/inputs/acheron.in create mode 100755 share/run/UA/inputs/aether.json.acheron create mode 100755 share/run/UA/inputs/aurora_acheron.csv create mode 100644 share/run/UA/inputs/chemistry_acheron.csv diff --git a/share/run/UA/inputs/acheron.in b/share/run/UA/inputs/acheron.in new file mode 100755 index 00000000..4c8adbf1 --- /dev/null +++ b/share/run/UA/inputs/acheron.in @@ -0,0 +1,55 @@ + +This is the input file for the planet Acheron. It is basically +meant to be an experimental planet. As a starting point, +Acheron will be a single neutral species (O) and +a single ion species (O+) planet. + +The base altitude of Acheron is 1000 km. + +name is the character string that identifies the species + +O(3P) is the base state of neutral atomic oxygen and is called O. + +Vibration is the degrees of freedom for the atom/molecule + +thermal conduction is typically: +Lambda = (N1 * A1 * T^c1 + N2 * A2 * T^c2 + ... ) / (N1+N2+...) +thermal_cond is the A for the particular species +thermal_exp is the c for the particular species + +mass is in amu + +advect is whether the species is advected or not + +BC is a density that is used in the lowest boundary cell if you + want a constant boundary condition. + Set to a real value to use this. + Set to a negative number if you want to use a different BC. + In this example file, the values are from 96.87 km Jan 1, 2013 + O_1D is made up. + + +#NEUTRALS +name, mass, vibration, thermal_cond, thermal_exp, advect, BC +O, 16, 5, 5.6e-4, 0.69, 1, 5.0e17 + + +Here we can input a temperature profile for the initial condition: + - below the lowest altitude, it will use the lowest alt + - above the highest alt, it will use the highest alt + - in between, linear interpolation + - altitudes in km + +#TEMPERATURE +alt, temp +1000.0, 200.0 +1500.0, 1000.0 +2000.0, 1000.0 + +O+ is O+(4S) is the base state of O+, but is just called O+ +O+ has excited states of O+(2D) and O+(2P). + +#IONS +name, mass, charge, advect +O+, 16, 1, 1 + diff --git a/share/run/UA/inputs/aether.json.acheron b/share/run/UA/inputs/aether.json.acheron new file mode 100755 index 00000000..7b3d77d5 --- /dev/null +++ b/share/run/UA/inputs/aether.json.acheron @@ -0,0 +1,58 @@ + +{ + "Debug" : { + "iVerbose" : 0, + "iFunctionVerbose" : { + "Grid::create_altitudes": 0}, + "dt" : 10.0, + "check_for_nans" : true + }, + + "Planet" : { + "name" : "acheron", + "file": "UA/inputs/acheron.in"}, + + "PlanetFile" : "UA/inputs/acheron.in", + + "ChemistryFile" : "UA/inputs/chemistry_acheron.csv", + + "Electrodynamics" : { + "Potential" : "zero", + "DiffuseAurora" : "none"}, + + "Logfile" : { + "species" : ["O", "O+"]}, + + "AuroraFile" : "UA/inputs/aurora_acheron.csv", + + "Advection" : { + "Neutrals" : { + "Vertical" : "hydro", + "Horizontal" : "default"}}, + + "Sources" : { + "Grid" : { + "Cent_acc" : false }}, + + "EndTime" : [2011, 3, 21, 0, 0, 0], + + "CubeSphere" : { + "is" : false}, + + "GeoBlockSize" : { + "nLons" : 144, + "nLats" : 72, + "nAlts" : 1}, + + "GeoGrid" : { + "MinAlt" : 1000.0, + "dAlt" : 0.25, + "IsUniformAlt" : false}, + + "Outputs" : { + "type" : ["states"], + "dt" : [900] }, + + "DoCalcBulkIonTemp" : false + +} diff --git a/share/run/UA/inputs/aurora_acheron.csv b/share/run/UA/inputs/aurora_acheron.csv new file mode 100755 index 00000000..613fb01b --- /dev/null +++ b/share/run/UA/inputs/aurora_acheron.csv @@ -0,0 +1 @@ +O,O+,1.0 diff --git a/share/run/UA/inputs/chemistry_acheron.csv b/share/run/UA/inputs/chemistry_acheron.csv new file mode 100644 index 00000000..44913d10 --- /dev/null +++ b/share/run/UA/inputs/chemistry_acheron.csv @@ -0,0 +1,3 @@ +name,loss1,loss2,loss3,->,source1,source2,source3,rate,temp dependent,Temp range,branching,heat,uncertainty,Numerator,Denominator,Exponent,Piecewise,Min,Max,FormulaType +reaction,name,name,name,goes to,name,name,name,mks,,,ratio,eV,,K,T,dimensionless,T,K,K,, +R1,O+,e-,,,O,,,1.00E-15,,,,0,0.1,,,,,,,, From e013e89499a0ca57b724bf283d7641d37e8965a6 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 08:26:48 -0500 Subject: [PATCH 044/127] STY: changed name of function --- src/main/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/main.cpp b/src/main/main.cpp index f10d5025..76137e93 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -79,7 +79,7 @@ int main() { // Calculate centripetal acceleration, since this is a constant // vector on the grid: - if (input.get_cent_acc()) + if (input.get_use_centripetal()) gGrid.calc_cent_acc(planet); // Initialize Magnetic grid: From 15a5f854b83f7e0d49e13e7aa725437dba152d5b Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 13 Dec 2023 08:27:29 -0500 Subject: [PATCH 045/127] BUG: initialize variables so output has correct shape --- src/neutrals.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 6df7e8d3..e16276d3 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -101,10 +101,6 @@ Neutrals::Neutrals(Grid grid, temperature_scgc.ones(); newTemperature_scgc.set_size(nLons, nLats, nAlts); newTemperature_scgc.ones(); - O_cool_scgc.set_size(nLons, nLats, nAlts); - O_cool_scgc.zeros(); - NO_cool_scgc.set_size(nLons, nLats, nAlts); - NO_cool_scgc.zeros(); // Derived quantities: @@ -137,10 +133,19 @@ Neutrals::Neutrals(Grid grid, viscosity_scgc.zeros(); conduction_scgc.set_size(nLons, nLats, nAlts); + conduction_scgc.zeros(); heating_euv_scgc.set_size(nLons, nLats, nAlts); + heating_euv_scgc.zeros(); heating_chemical_scgc.set_size(nLons, nLats, nAlts); + heating_chemical_scgc.zeros(); heating_sources_total.set_size(nLons, nLats, nAlts); heating_sources_total.zeros(); + heating_ion_collisions_scgc.set_size(nLons, nLats, nAlts); + heating_ion_collisions_scgc.zeros(); + O_cool_scgc.set_size(nLons, nLats, nAlts); + O_cool_scgc.zeros(); + NO_cool_scgc.set_size(nLons, nLats, nAlts); + NO_cool_scgc.zeros(); heating_efficiency = input.get_euv_heating_eff_neutrals(); From bf594585796f7be1b9c8782a3a289564b2f70f5e Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 15 Dec 2023 06:55:00 -0500 Subject: [PATCH 046/127] FEAT: provide simulation time --- include/times.h | 5 +++++ src/time.cpp | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/include/times.h b/include/times.h index 3f2e8fe4..f0fff47e 100644 --- a/include/times.h +++ b/include/times.h @@ -148,6 +148,11 @@ class Times { **/ std::vector get_iCurrent(); + /************************************************************** + \brief Get the current simulation time (sec since start) + **/ + double get_simulation_time(); + /********************************************************************** \brief Read / Write restart files for time \param dir directory to write restart files diff --git a/src/time.cpp b/src/time.cpp index 86bbd6bb..d0962e33 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -175,6 +175,14 @@ double Times::get_julian_day() { return julian_day; } +// ----------------------------------------------------------------------------- +// Get the current simulation time (in sec since start of run) +// ----------------------------------------------------------------------------- + +double Times::get_simulation_time() { + return simulation; +} + // ----------------------------------------------------------------------------- // Get the current time array // ----------------------------------------------------------------------------- From 1d0e66733a68f63d108e091e195c7e6a0bfca0b0 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 15 Dec 2023 06:56:09 -0500 Subject: [PATCH 047/127] TEST: trying to figure out instability --- src/add_sources.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index 37916aff..fd7742f6 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -31,6 +31,65 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { std::vector acc_coriolis; acc_coriolis = make_cube_vector(grid.get_nX(), grid.get_nY(), grid.get_nZ(), 3); + int64_t iDir, iSpec, iSpecies; + double tSim = time.get_simulation_time(); + precision_t ramp = tSim / 3600.0; + if (ramp > 1.0) ramp = 1.0; + + // Vertical winds use species winds: + for (iSpec = 0; iSpec < nSpeciesAdvect; iSpec++) { + // Pick out the advected neutral species: + species_chars & advected_neutral = species[species_to_advect[iSpec]]; + // Calculate Coriolis: + if (input.get_use_coriolis()) + acc_coriolis = coriolis(advected_neutral.velocity_vcgc, + planet.get_omega(), + grid.geoLat_scgc); + + iDir = 2; + // update velocities based on acceleration: + // reduce neutral friction until solver is added + advected_neutral.velocity_vcgc[iDir] = + advected_neutral.velocity_vcgc[iDir] + + dt * (ramp * grid.cent_acc_vcgc[iDir] + + ramp * acc_coriolis[iDir] + + advected_neutral.acc_neutral_friction[iDir] / 4.0 + + advected_neutral.acc_ion_drag[iDir] + + advected_neutral.acc_eddy); + } + + calc_mass_density(); + // Calculate bulk vertical winds: + velocity_vcgc[2].zeros(); + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + if (species[iSpecies].DoAdvect) { + velocity_vcgc[2] = velocity_vcgc[2] + + species[iSpecies].mass * species[iSpecies].density_scgc % + species[iSpecies].velocity_vcgc[2] / rho_scgc; + } + + + // Horizontal winds use bulk winds: + if (input.get_use_coriolis()) + acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); + // Add Velocity sources to bulk winds: + for (iDir = 0; iDir < 2; iDir++) { + velocity_vcgc[iDir] = + velocity_vcgc[iDir] + dt * ( + ramp * grid.cent_acc_vcgc[iDir] + + ramp * acc_coriolis[iDir] + + acc_ion_collisions[iDir]); + acc_sources_total[iDir].zeros(); + } + // Apply Viscosity: + update_horizontal_velocity(grid, time); + + // Assign bulk horizontal velocity to all species: + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + for (iDir = 0; iDir < 2; iDir++) + species[iSpecies].velocity_vcgc[iDir] = velocity_vcgc[iDir]; + + /* // If we only consider the bulk winds in the horizontal direction: if (input.get_advection_neutrals_bulkwinds()) { // Calculate Coriolis: @@ -75,7 +134,7 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { calc_bulk_velocity(); } assign_bulk_velocity(); - + */ report.exit(function); return; } From 7047b22c813bdb2b7ca2d13a4038cd0c9e6b42f8 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 15 Dec 2023 06:57:06 -0500 Subject: [PATCH 048/127] FEAT: assign winds to each species --- src/solver_advection.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/solver_advection.cpp b/src/solver_advection.cpp index 3f89e982..6eb6ae69 100644 --- a/src/solver_advection.cpp +++ b/src/solver_advection.cpp @@ -546,6 +546,11 @@ void advect(Grid &grid, } neutrals.calc_density_from_mass_concentration(); + // Assign bulk horizontal velocity to all species: + for (int64_t iSpecies = 0; iSpecies < neutrals.nSpecies; iSpecies++) + for (int64_t iDir = 0; iDir < 2; iDir++) + neutrals.species[iSpecies].velocity_vcgc[iDir] = neutrals.velocity_vcgc[iDir]; + report.exit(function); return; } From afaa5e8ff69c47f3af3d65820ac72ec35545aae8 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Fri, 15 Dec 2023 06:57:59 -0500 Subject: [PATCH 049/127] TEST: trying to figure out instability --- src/solver_coriolis.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/solver_coriolis.cpp b/src/solver_coriolis.cpp index 0ab125f7..4a17897e 100644 --- a/src/solver_coriolis.cpp +++ b/src/solver_coriolis.cpp @@ -14,10 +14,10 @@ std::vector coriolis(std::vector velocity, precision_t rotation_rate, arma_cube lat_scgc) { std::vector coriolis_vec(3); - coriolis_vec[0] = - 2 * rotation_rate * velocity[1] % sin(lat_scgc) - - 2 * rotation_rate * velocity[2] % cos(lat_scgc); + coriolis_vec[0] = ( + 2 * rotation_rate * velocity[1] % sin(lat_scgc)); // - + //2 * rotation_rate * velocity[2] % cos(lat_scgc) ); coriolis_vec[1] = -2 * rotation_rate * velocity[0] % sin(lat_scgc); - coriolis_vec[2] = 2 * rotation_rate * cos(lat_scgc) % velocity[1]; + coriolis_vec[2] = 2 * rotation_rate * cos(lat_scgc) % velocity[1] * 0.0; return coriolis_vec; } From 4282855e72f2bc38c8886564fca1b226574faea7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:21:42 -0500 Subject: [PATCH 050/127] BUG: moved into neutrals and the vertical solver --- include/calc_momentum_friction.h | 8 +-- src/neutrals_momentum_friction.cpp | 83 +++++++++++++++--------------- 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/include/calc_momentum_friction.h b/include/calc_momentum_friction.h index 24a6e9ef..6d302fb7 100644 --- a/include/calc_momentum_friction.h +++ b/include/calc_momentum_friction.h @@ -4,11 +4,8 @@ #ifndef INCLUDE_CALC_MOMENTUM_FRICTION_H_ #define INCLUDE_CALC_MOMENTUM_FRICTION_H_ -arma_vec neutral_friction_one_cell(int64_t iLong, int64_t iLat, int64_t iAlt, - arma_vec &vels, - Neutrals &neutrals); +#include "../include/aether.h" -void calc_neutral_friction(Neutrals &neutrals); /********************************************************************** \brief Calculate acceleration due to ion drag @@ -17,7 +14,6 @@ void calc_neutral_friction(Neutrals &neutrals); \param dt The change in time \param report allow reporting to occur **/ -void calc_ion_collisions(Neutrals &neutrals, - Ions &ions); +void calc_ion_collisions(Neutrals &neutrals, Ions &ions); #endif // INCLUDE_CALC_MOMENTUM_FRICTION_H_ diff --git a/src/neutrals_momentum_friction.cpp b/src/neutrals_momentum_friction.cpp index 30493882..e92a3f34 100644 --- a/src/neutrals_momentum_friction.cpp +++ b/src/neutrals_momentum_friction.cpp @@ -10,34 +10,30 @@ // multiply by dt later. // --------------------------------------------------------------------- -arma_vec neutral_friction_one_cell(int64_t iLon, int64_t iLat, int64_t iAlt, - arma_vec &vels, - Neutrals &neutrals) { +arma_vec Neutrals::calc_friction_one_cell(int64_t iLon, int64_t iLat, int64_t iAlt, + arma_vec &vels) { std::string function = "neutral_friction_one_cell"; static int iFunction = -1; report.enter(function, iFunction); precision_t ktom, temp_dij; int64_t iSpecies, jSpecies, iSpecies_, jSpecies_; - static arma_mat matrix(neutrals.nSpeciesAdvect, neutrals.nSpeciesAdvect, - fill::zeros); - static arma_mat coefmatrix(neutrals.nSpeciesAdvect, neutrals.nSpeciesAdvect, - fill::zeros); + static arma_mat matrix(nSpeciesAdvect, nSpeciesAdvect, fill::zeros); + static arma_mat coefmatrix(nSpeciesAdvect, nSpeciesAdvect, fill::zeros); - for (iSpecies = 0; iSpecies < neutrals.nSpeciesAdvect; iSpecies++) { - iSpecies_ = neutrals.species_to_advect[iSpecies]; + for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) { + iSpecies_ = species_to_advect[iSpecies]; - Neutrals::species_chars & advected_neutral = - neutrals.species[iSpecies_]; + Neutrals::species_chars & advected_neutral = species[iSpecies_]; // ktom = boltzmann's constant * temperature / mass ktom = cKB * - neutrals.temperature_scgc(iLon, iLat, iAlt) / + temperature_scgc(iLon, iLat, iAlt) / advected_neutral.mass; - for (jSpecies = 0; jSpecies < neutrals.nSpeciesAdvect; jSpecies++) { - jSpecies_ = neutrals.species_to_advect[jSpecies]; + for (jSpecies = 0; jSpecies < nSpeciesAdvect; jSpecies++) { + jSpecies_ = species_to_advect[jSpecies]; if (iSpecies_ == jSpecies_) continue; @@ -48,41 +44,42 @@ arma_vec neutral_friction_one_cell(int64_t iLon, int64_t iLat, int64_t iAlt, // (2) Additionally, the Dij's are in cm^2/s, thus the 1.0e-04 factor temp_dij = 1e-4 * advected_neutral.diff0[jSpecies_] * - pow(neutrals.temperature_scgc(iLon, iLat, iAlt), + pow(temperature_scgc(iLon, iLat, iAlt), advected_neutral.diff_exp[jSpecies_]) / - (neutrals.density_scgc(iLon, iLat, iAlt) * 1e-6); + (density_scgc(iLon, iLat, iAlt) * 1e-6); coefmatrix(iSpecies, jSpecies) = ktom * advected_neutral.density_scgc(iLon, iLat, iAlt) / - (temp_dij * neutrals.density_scgc(iLon, iLat, iAlt)); + (temp_dij * density_scgc(iLon, iLat, iAlt)); } // jSpec loop } // iSpec loop matrix = -1 * coefmatrix; // Fill in diagonal of matrix: - for (iSpecies = 0; iSpecies < neutrals.nSpeciesAdvect; iSpecies++) + for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) matrix(iSpecies, iSpecies) = 1 + sum(coefmatrix.row(iSpecies)); // initialize array of each neutral species' accelerations at (iLon, iLat, iAlt): - arma_vec accs(neutrals.nSpeciesAdvect, fill::zeros); + arma_vec accs(nSpeciesAdvect, fill::zeros); // Solve system of equations: arma_vec new_vels = arma::solve(matrix, vels, solve_opts::fast); + /* // put the new values into the velocity cubes: - for (iSpecies = 0; iSpecies < neutrals.nSpeciesAdvect; iSpecies++) + for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) accs(iSpecies) = new_vels(iSpecies) - vels(iSpecies); - + */ report.exit(function); - return accs; + return new_vels; } // --------------------------------------------------------------------- // // --------------------------------------------------------------------- -void calc_neutral_friction(Neutrals &neutrals) { +void Neutrals::calc_neutral_friction() { std::string function = "calc_neutral_friction"; static int iFunction = -1; @@ -91,42 +88,46 @@ void calc_neutral_friction(Neutrals &neutrals) { int64_t iAlt, iLat, iLon, iDir, iSpecies, iSpecies_; // Initialize all of the accelerations to zero: - for (iSpecies = 0; iSpecies < neutrals.nSpecies; iSpecies++) + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) for (iDir = 0; iDir < 3; iDir++) - neutrals.species[iSpecies].acc_neutral_friction[iDir].zeros(); + species[iSpecies].acc_neutral_friction[iDir].zeros(); if (input.get_advection_neutrals_vertical() != "hydro") { - arma_vec vels(neutrals.nSpeciesAdvect, fill::zeros); - arma_vec acc(neutrals.nSpeciesAdvect, fill::zeros); - int64_t nXs = neutrals.temperature_scgc.n_rows; - int64_t nYs = neutrals.temperature_scgc.n_cols; - int64_t nZs = neutrals.temperature_scgc.n_slices; + arma_vec vels(nSpeciesAdvect, fill::zeros); + arma_vec acc(nSpeciesAdvect, fill::zeros); + arma_vec new_vels; + int64_t nXs = temperature_scgc.n_rows; + int64_t nYs = temperature_scgc.n_cols; + int64_t nZs = temperature_scgc.n_slices; // Calculate friction terms for only species that advect. // - If only 1 species is advected, then it will have no friction - if (neutrals.nSpeciesAdvect > 1) { + if (nSpeciesAdvect > 1) { for (iAlt = 0; iAlt < nZs; iAlt++) { for (iLat = 0; iLat < nYs; iLat++) { for (iLon = 0; iLon < nXs; iLon++) { - for (iDir = 0; iDir < 3; iDir++) { + iDir = 2; + //for (iDir = 0; iDir < 3; iDir++) { vels.zeros(); //Put the old velocities into vels: - for (iSpecies = 0; iSpecies < neutrals.nSpeciesAdvect; iSpecies++) { - iSpecies_ = neutrals.species_to_advect[iSpecies]; + for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) { + iSpecies_ = species_to_advect[iSpecies]; vels(iSpecies) = - neutrals.species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt); + species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt); } - acc = neutral_friction_one_cell(iLon, iLat, iAlt, vels, neutrals); + //acc = neutral_friction_one_cell(iLon, iLat, iAlt, vels); + new_vels = calc_friction_one_cell(iLon, iLat, iAlt, vels); - for (iSpecies = 0; iSpecies < neutrals.nSpeciesAdvect; iSpecies++) { - iSpecies_ = neutrals.species_to_advect[iSpecies]; - neutrals.species[iSpecies_].acc_neutral_friction[iDir](iLon, iLat, iAlt) = - acc(iSpecies); + for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) { + iSpecies_ = species_to_advect[iSpecies]; + species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt) = new_vels(iSpecies); + //species[iSpecies_].acc_neutral_friction[iDir](iLon, iLat, iAlt) = + // acc(iSpecies); } // iSpeciesAdvect - } // for direction + //} // for direction } // for long } // for lat } // for alt From e3e8e851d84d79dea3d6519621ceb4b329e06e62 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:22:04 -0500 Subject: [PATCH 051/127] BUG: moved into neutrals and the vertical solver --- include/neutrals.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/neutrals.h b/include/neutrals.h index 8b438074..459a96ae 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -191,6 +191,9 @@ class Neutrals { // Bulk acceleration due to collisions with ions: std::vector acc_ion_collisions; + // Bulk acceleration due to coriolis + std::vector acc_coriolis; + // Total bulk acceleration std::vector acc_sources_total; @@ -524,6 +527,11 @@ class Neutrals { **/ bool advect_vertical(Grid grid, Times time); + + arma_vec calc_friction_one_cell(int64_t iLong, int64_t iLat, int64_t iAlt, + arma_vec &vels); + + void calc_neutral_friction(); }; From 03fc8fb2440a8a7343eb75ad46530d71b3d25627 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:22:50 -0500 Subject: [PATCH 052/127] FEAT: save coriolis --- src/neutrals.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/neutrals.cpp b/src/neutrals.cpp index e16276d3..2058bdcf 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -151,6 +151,8 @@ Neutrals::Neutrals(Grid grid, // bulk ion_neutral collisional acceleration: acc_ion_collisions = make_cube_vector(nLons, nLats, nAlts, 3); + // bulk coriolis acceleration: + acc_coriolis = make_cube_vector(nLons, nLats, nAlts, 3); // bulk ion_neutral collisional acceleration: acc_sources_total = make_cube_vector(nLons, nLats, nAlts, 3); From 7b9168fa9c2fd5bba563d816169120619106d34c Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:23:28 -0500 Subject: [PATCH 053/127] TEST: add back in dropped terms --- src/solver_coriolis.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_coriolis.cpp b/src/solver_coriolis.cpp index 4a17897e..46411fd5 100644 --- a/src/solver_coriolis.cpp +++ b/src/solver_coriolis.cpp @@ -15,8 +15,8 @@ std::vector coriolis(std::vector velocity, arma_cube lat_scgc) { std::vector coriolis_vec(3); coriolis_vec[0] = ( - 2 * rotation_rate * velocity[1] % sin(lat_scgc)); // - - //2 * rotation_rate * velocity[2] % cos(lat_scgc) ); + 2 * rotation_rate * velocity[1] % sin(lat_scgc) - + 2 * rotation_rate * velocity[2] % cos(lat_scgc)); coriolis_vec[1] = -2 * rotation_rate * velocity[0] % sin(lat_scgc); coriolis_vec[2] = 2 * rotation_rate * cos(lat_scgc) % velocity[1] * 0.0; return coriolis_vec; From ec4287118b5957235733acc5325e451b9224bbe7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:24:14 -0500 Subject: [PATCH 054/127] TEST: add back in terms and move others to solvers --- src/add_sources.cpp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index fd7742f6..dbae9f33 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -28,23 +28,18 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { // updating the neutral temperature: update_temperature(grid, time); - std::vector acc_coriolis; - acc_coriolis = make_cube_vector(grid.get_nX(), grid.get_nY(), grid.get_nZ(), 3); - int64_t iDir, iSpec, iSpecies; double tSim = time.get_simulation_time(); - precision_t ramp = tSim / 3600.0; - if (ramp > 1.0) ramp = 1.0; + // Horizontal winds use bulk winds: + if (input.get_use_coriolis()) + acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); + +/* // Vertical winds use species winds: for (iSpec = 0; iSpec < nSpeciesAdvect; iSpec++) { // Pick out the advected neutral species: species_chars & advected_neutral = species[species_to_advect[iSpec]]; - // Calculate Coriolis: - if (input.get_use_coriolis()) - acc_coriolis = coriolis(advected_neutral.velocity_vcgc, - planet.get_omega(), - grid.geoLat_scgc); iDir = 2; // update velocities based on acceleration: @@ -68,16 +63,14 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { species[iSpecies].velocity_vcgc[2] / rho_scgc; } - - // Horizontal winds use bulk winds: - if (input.get_use_coriolis()) - acc_coriolis = coriolis(velocity_vcgc, planet.get_omega(), grid.geoLat_scgc); + */ + // Add Velocity sources to bulk winds: for (iDir = 0; iDir < 2; iDir++) { velocity_vcgc[iDir] = velocity_vcgc[iDir] + dt * ( - ramp * grid.cent_acc_vcgc[iDir] + - ramp * acc_coriolis[iDir] + + grid.cent_acc_vcgc[iDir] + + acc_coriolis[iDir] + acc_ion_collisions[iDir]); acc_sources_total[iDir].zeros(); } From cf1eafdbdad8a2c66ad4bdc84315b01bac71e411 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:24:52 -0500 Subject: [PATCH 055/127] BUG: move to vertical solver --- src/advance.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index a125fc94..31cb7a8b 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -107,9 +107,7 @@ bool advance(Planets &planet, if (input.get_NO_cooling()) neutrals.calc_NO_cool(); - neutrals.vertical_momentum_eddy(gGrid); calc_ion_collisions(neutrals, ions); - calc_neutral_friction(neutrals); neutrals.add_sources(time, planet, gGrid); From f84979741033095ef872790e89d700443a10753b Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 10:26:05 -0500 Subject: [PATCH 056/127] TEST: put friction into solver --- src/solver_vertical_rusanov.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/solver_vertical_rusanov.cpp b/src/solver_vertical_rusanov.cpp index d517e604..e9181352 100644 --- a/src/solver_vertical_rusanov.cpp +++ b/src/solver_vertical_rusanov.cpp @@ -251,6 +251,10 @@ void Neutrals::solver_vertical_rusanov(Grid grid, v2or = (velocity_vcgc[0] % velocity_vcgc[0] + velocity_vcgc[1] % velocity_vcgc[1]) / grid.radius_scgc; + // calculate vertical momentum due to eddy diffusion: + vertical_momentum_eddy(grid); + + // ----------------------------------------------------------- // Now calculate new states: precision_t mass; @@ -277,7 +281,10 @@ void Neutrals::solver_vertical_rusanov(Grid grid, species[iSpecies].velocity_vcgc[2] - dt * (species[iSpecies].velocity_vcgc[2] % gradVertVel_s[iSpecies] - v2or - + 0.05 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + - species[iSpecies].acc_eddy + - acc_coriolis[2] + - grid.cent_acc_vcgc[2] + + 0.025 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + gradTemp * cKB / mass + abs(grid.gravity_vcgc[2]))) + dt * diffVertVel_s[iSpecies]; @@ -287,6 +294,9 @@ void Neutrals::solver_vertical_rusanov(Grid grid, } } + + + newTemperature_scgc = temperature_scgc - dt * (velocity_vcgc[2] % gradTemp @@ -317,6 +327,17 @@ void Neutrals::solver_vertical_rusanov(Grid grid, } } + // Force the neutrals to move together with friction: + calc_neutral_friction(); + /* + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + if (species[iSpecies].DoAdvect) { + species[iSpecies].velocity_vcgc[2] = + species[iSpecies].velocity_vcgc[2] + dt * + species[iSpecies].acc_neutral_friction[iDir]; + } + } + */ calc_mass_density(); // Calculate bulk vertical winds: velocity_vcgc[2].zeros(); From 26df7750adea5f551ffbc8a8e2a41350d9add747 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 14:07:00 -0500 Subject: [PATCH 057/127] FEAT: add message for nan check --- include/neutrals.h | 2 +- src/advance.cpp | 13 ++++++++----- src/main/main.cpp | 2 +- src/neutrals.cpp | 5 ++++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/neutrals.h b/include/neutrals.h index 459a96ae..e041e758 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -451,7 +451,7 @@ class Neutrals { /***************************************************************************** \brief Checks for nans and +/- infinities in density, temp, and velocity **/ - bool check_for_nonfinites(); + bool check_for_nonfinites(std::string location); /********************************************************************** \brief Checks for nans in the specified variable diff --git a/src/advance.cpp b/src/advance.cpp index 31cb7a8b..30af8508 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -36,7 +36,7 @@ bool advance(Planets &planet, input.get_student_name() + "?"); if (didWork & input.get_check_for_nans()) - didWork = neutrals.check_for_nonfinites(); + didWork = neutrals.check_for_nonfinites("Top of Advance"); gGrid.calc_sza(planet, time); neutrals.calc_mass_density(); @@ -67,11 +67,14 @@ bool advance(Planets &planet, neutrals.exchange_old(gGrid); advect(gGrid, time, neutrals); + if (didWork & input.get_check_for_nans()) + didWork = neutrals.check_for_nonfinites("After Horizontal Advection"); + if (input.get_nAltsGeo() > 1) neutrals.advect_vertical(gGrid, time); if (didWork & input.get_check_for_nans()) - didWork = neutrals.check_for_nonfinites(); + didWork = neutrals.check_for_nonfinites("After Vertical Advection"); // ------------------------------------ // Calculate source terms next: @@ -111,6 +114,9 @@ bool advance(Planets &planet, neutrals.add_sources(time, planet, gGrid); + if (didWork & input.get_check_for_nans()) + didWork = neutrals.check_for_nonfinites("After Add Sources"); + ions.calc_ion_temperature(neutrals, gGrid, time); ions.calc_electron_temperature(neutrals, gGrid); @@ -129,9 +135,6 @@ bool advance(Planets &planet, } } - if (didWork & input.get_check_for_nans()) - didWork = neutrals.check_for_nonfinites(); - if (didWork) didWork = output(neutrals, ions, gGrid, time, planet); diff --git a/src/main/main.cpp b/src/main/main.cpp index 76137e93..9e5d2ddb 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -103,7 +103,7 @@ int main() { } if (input.get_check_for_nans()) { - didWork = neutrals.check_for_nonfinites(); + didWork = neutrals.check_for_nonfinites("In Main, before main loop"); didWork = ions.check_for_nonfinites(); } diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 2058bdcf..8f79ff48 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -307,7 +307,7 @@ void Neutrals::nan_test(std::string variable) { // Checks for nans and +/- infinities in density, temp, and velocity //---------------------------------------------------------------------- -bool Neutrals::check_for_nonfinites() { +bool Neutrals::check_for_nonfinites(std::string location) { bool isBad = false; bool didWork = true; @@ -315,6 +315,7 @@ bool Neutrals::check_for_nonfinites() { if (isBad) { report.error("non-finite found in neutral density!"); + report.error("from location : " + location); didWork = false; } @@ -322,6 +323,7 @@ bool Neutrals::check_for_nonfinites() { if (isBad) { report.error("non-finite found in neutral temperature!"); + report.error("from location : " + location); didWork = false; } @@ -329,6 +331,7 @@ bool Neutrals::check_for_nonfinites() { if (isBad) { report.error("non-finite found in neutral velocity!"); + report.error("from location : " + location); didWork = false; } From 469b616208c433ada89baa17b712fe28ad99aa02 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 14:38:29 -0500 Subject: [PATCH 058/127] BUG: need to sync up didwork in case one PE goes bad --- src/neutrals.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 8f79ff48..48192a9a 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -334,7 +334,8 @@ bool Neutrals::check_for_nonfinites(std::string location) { report.error("from location : " + location); didWork = false; } - + didWork = sync_across_all_procs(didWork); + return didWork; } From efe78627f718925720026a1ff226342cd38bb02e Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 15 Dec 2023 14:39:13 -0500 Subject: [PATCH 059/127] TEST: move vertical solver to after temperature conduction --- src/advance.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index 30af8508..c5fe05c4 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -70,12 +70,6 @@ bool advance(Planets &planet, if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites("After Horizontal Advection"); - if (input.get_nAltsGeo() > 1) - neutrals.advect_vertical(gGrid, time); - - if (didWork & input.get_check_for_nans()) - didWork = neutrals.check_for_nonfinites("After Vertical Advection"); - // ------------------------------------ // Calculate source terms next: @@ -120,11 +114,20 @@ bool advance(Planets &planet, ions.calc_ion_temperature(neutrals, gGrid, time); ions.calc_electron_temperature(neutrals, gGrid); + if (input.get_nAltsGeo() > 1) + neutrals.advect_vertical(gGrid, time); + + if (didWork & input.get_check_for_nans()) + didWork = neutrals.check_for_nonfinites("After Vertical Advection"); + + if (input.get_is_cubesphere()) neutrals.exchange(gGrid); else neutrals.exchange_old(gGrid); + + time.increment_time(); if (time.check_time_gate(input.get_dt_write_restarts())) { From c8da4cc559041b770af9aaa18fbcf148734d18f0 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Tue, 26 Dec 2023 17:12:17 -0500 Subject: [PATCH 060/127] FEAT: test all species for NaNs --- src/neutrals.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 48192a9a..4e838644 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -312,15 +312,25 @@ bool Neutrals::check_for_nonfinites(std::string location) { bool didWork = true; isBad = !all_finite(density_scgc, "density_scgc"); - if (isBad) { report.error("non-finite found in neutral density!"); report.error("from location : " + location); didWork = false; } + int64_t iSpecies; + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + isBad = !all_finite(species[iSpecies].density_scgc, + species[iSpecies].cName + " density"); + if (isBad) { + report.error("non-finite found in " + + species[iSpecies].cName + " density!"); + report.error("from location : " + location); + didWork = false; + } + } + isBad = !all_finite(temperature_scgc, "temperature_scgc"); - if (isBad) { report.error("non-finite found in neutral temperature!"); report.error("from location : " + location); @@ -328,13 +338,23 @@ bool Neutrals::check_for_nonfinites(std::string location) { } isBad = !all_finite(velocity_vcgc, "velocity_vcgc"); - if (isBad) { report.error("non-finite found in neutral velocity!"); report.error("from location : " + location); didWork = false; } didWork = sync_across_all_procs(didWork); + + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + isBad = !all_finite(species[iSpecies].velocity_vcgc, + species[iSpecies].cName + " velocity!"); + if (isBad) { + report.error("non-finite found in " + + species[iSpecies].cName + " velocity!"); + report.error("from location : " + location); + didWork = false; + } + } return didWork; } From e5b0117bce2e6d4e450b56c6db55b95b56c99116 Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Tue, 26 Dec 2023 17:13:34 -0500 Subject: [PATCH 061/127] FEAT: fill everything with hydrostatic initially --- src/neutrals_ics.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/neutrals_ics.cpp b/src/neutrals_ics.cpp index 9f67e005..02fb258f 100644 --- a/src/neutrals_ics.cpp +++ b/src/neutrals_ics.cpp @@ -167,7 +167,9 @@ bool Neutrals::initial_conditions(Grid grid, } calc_scale_height(grid); - fill_with_hydrostatic(1, nAlts, grid); + for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) + fill_with_hydrostatic(iSpecies, 1, nAlts, grid); + } // type = planet } From 666bc49be68d17ec03078a446d5c7507698fd65c Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Tue, 26 Dec 2023 17:16:24 -0500 Subject: [PATCH 062/127] TEST: remove limiters to see what happens --- src/solver_advection.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/solver_advection.cpp b/src/solver_advection.cpp index 6eb6ae69..85b6dbfe 100644 --- a/src/solver_advection.cpp +++ b/src/solver_advection.cpp @@ -516,24 +516,26 @@ void advect(Grid &grid, neutrals.velocity_vcgc[0].slice(iAlt) = xVel; neutrals.velocity_vcgc[1].slice(iAlt) = yVel; temp = (totalE / rho - 0.5 * (xVel % xVel + yVel % yVel)) / t_to_e; - precision_t fac, dm, dp; + temp.clamp(200, 2000); + + //precision_t fac, dm, dp; for (int64_t j = nGCs; j < nY - nGCs; j++) { for (int64_t i = nGCs; i < nX - nGCs; i++) { - fac = 1.0; - if (cos(grid.geoLat_scgc(i,j,iAlt)) < 0.2) { - fac = fac * (0.2 - cos(grid.geoLat_scgc(i,j,iAlt))); - } - dm = (1.0 - fac) * neutrals.temperature_scgc(i,j,iAlt); - dp = (1.0 + fac) * neutrals.temperature_scgc(i,j,iAlt); - if (temp(i,j) < dm) temp(i,j) = dm; - if (temp(i,j) > dp) temp(i,j) = dp; + //fac = 1.0; + //if (cos(grid.geoLat_scgc(i,j,iAlt)) < 0.2) { + // fac = fac * (0.2 - cos(grid.geoLat_scgc(i,j,iAlt))); + //} + //dm = (1.0 - fac) * neutrals.temperature_scgc(i,j,iAlt); + //dp = (1.0 + fac) * neutrals.temperature_scgc(i,j,iAlt); + //if (temp(i,j) < dm) temp(i,j) = dm; + //if (temp(i,j) > dp) temp(i,j) = dp; neutrals.temperature_scgc(i,j,iAlt) = temp(i,j); - dm = (1.0 - fac) * neutrals.rho_scgc(i,j,iAlt); - dp = (1.0 + fac) * neutrals.rho_scgc(i,j,iAlt); - if (rho(i,j) < dm) rho(i,j) = dm; - if (rho(i,j) > dp) rho(i,j) = dp; + //dm = (1.0 - fac) * neutrals.rho_scgc(i,j,iAlt); + //dp = (1.0 + fac) * neutrals.rho_scgc(i,j,iAlt); + //if (rho(i,j) < dm) rho(i,j) = dm; + //if (rho(i,j) > dp) rho(i,j) = dp; neutrals.rho_scgc(i,j,iAlt) = rho(i,j); } } From d05931c3b84df9bc0beb485085da04fba3c46c3c Mon Sep 17 00:00:00 2001 From: Aaron James Ridley Date: Tue, 26 Dec 2023 17:18:01 -0500 Subject: [PATCH 063/127] TEST: ease up on limiters --- src/solver_vertical_rusanov.cpp | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/src/solver_vertical_rusanov.cpp b/src/solver_vertical_rusanov.cpp index e9181352..74b8814c 100644 --- a/src/solver_vertical_rusanov.cpp +++ b/src/solver_vertical_rusanov.cpp @@ -26,12 +26,14 @@ void calc_facevalues_alts_rusanov(Grid &grid, precision_t beta = 1.5; // inverse delta-alt at cell edge - arma_mat ida; + arma_mat ida(nXs, nYs); // Projection of variables to the cell edge: arma_mat dVarUp(nXs, nYs), dVarDown(nXs, nYs); arma_cube dVarLimited(nXs, nYs, nZs); + dVarLimited.zeros(); + // Only do calculation on physical cells for (iZ = nGCs; iZ < nZs - nGCs; iZ++) { ida = 2.0 / grid.dalt_lower_scgc.slice(iZ + 1); @@ -75,6 +77,20 @@ void calc_facevalues_alts_rusanov(Grid &grid, dVarLimited(iX, iY, iZ) = limiter_mc(dVarUp(iX, iY), dVarDown(iX, iY), beta); + // TEMPORARY!!! + iZ = 18; + ida = 2.0 / grid.dalt_lower_scgc.slice(iZ + 1); + dVarUp = ida % + (factor1 * (inVar.slice(iZ + 1) - inVar.slice(iZ)) - + factor2 * (inVar.slice(iZ + 2) - inVar.slice(iZ - 1))); + + ida = 2.0 / grid.dalt_lower_scgc.slice(iZ); + dVarDown = ida % + (factor1 * (inVar.slice(iZ) - inVar.slice(iZ - 1)) - + factor2 * (inVar.slice(iZ + 1) - inVar.slice(iZ - 2))); + + // End TEMP + for (iZ = nGCs; iZ < nZs - nGCs + 1; iZ++) { outLeft.slice(iZ) = inVar.slice(iZ - 1) + @@ -83,7 +99,23 @@ void calc_facevalues_alts_rusanov(Grid &grid, inVar.slice(iZ) - 0.5 * dVarLimited.slice(iZ) % grid.dalt_lower_scgc.slice(iZ); } - + /* + if (iProc == 11) + std::cout << "facevalues : " + << inVar(7,19,17) << " " + << inVar(7,19,18) << " " + << inVar(7,19,19) << " " + << inVar(7,19,20) << " " + << dVarLimited(7,19,18) << " " + << grid.dalt_lower_scgc(7,19,17) << " " + << outRight(7, 19, 17) << " " + << outRight(7, 19, 18) << " " + << outLeft(7, 19, 17) << " " + << outLeft(7, 19, 18) << " " + << dVarUp(7, 19) << " " + << dVarDown(7, 19) << "\n"; + */ + return; } @@ -124,7 +156,16 @@ void calc_grad_and_diff_alts_rusanov(Grid &grid, (varLeft.slice(iZ + 1) + varRight.slice(iZ + 1) - varLeft.slice(iZ) - varRight.slice(iZ)) / grid.dalt_center_scgc.slice(iZ); - + /* + if (iProc == 11) + std::cout << "calc_grad : " + << varLeft(7, 19, 17) << " " + << varLeft(7, 19, 18) << " " + << varRight(7, 19, 17) << " " + << varRight(7, 19, 18) << " " + << grid.dalt_center_scgc(7, 19, 17) << " " + << outGrad(7, 19, 17) << "\n"; + */ for (iZ = nGCs; iZ < nZs - nGCs + 1; iZ++) { for (iX = nGCs; iX < nXs - nGCs; iX++) for (iY = nGCs; iY < nYs - nGCs; iY++) { @@ -225,6 +266,20 @@ void Neutrals::solver_vertical_rusanov(Grid grid, cMax_vcgc[2], gradDummy, diffDummy); + /* + if (iProc == 11) + std::cout << "gradlog : " + << iSpecies << " " + << species[iSpecies].density_scgc(7,19,17) << " " + << log_s(7,19,14) << " " + << log_s(7,19,15) << " " + << log_s(7,19,16) << " " + << log_s(7,19,17) << " " + << log_s(7,19,18) << " " + << log_s(7,19,19) << " " + << cMax_vcgc[2](7,19,17) << " " + << gradDummy(7,19,17) << "\n"; + */ gradLogN_s[iSpecies] = gradDummy; diffLogN_s[iSpecies] = diffDummy; @@ -284,28 +339,57 @@ void Neutrals::solver_vertical_rusanov(Grid grid, - species[iSpecies].acc_eddy - acc_coriolis[2] - grid.cent_acc_vcgc[2] - + 0.025 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + + 0.25 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + gradTemp * cKB / mass + abs(grid.gravity_vcgc[2]))) + dt * diffVertVel_s[iSpecies]; + /* + if (iProc == 11) { + std::cout << "den + vel : " + << iSpecies << " " + << species[iSpecies].density_scgc(7,19,17) << " " + << log_s(7,19,17) << " " + << species[iSpecies].velocity_vcgc[2](7,19,17) << " " + << species[iSpecies].newVelocity_vcgc[2](7,19,17) << " " + << species[iSpecies].acc_eddy(7,19,17) << " " + << velocity_vcgc[0](7,19,17) << " " + << velocity_vcgc[1](7,19,17) << " " + << cMax_vcgc[0](7,19,17) << " " + << cMax_vcgc[1](7,19,17) << " " + << cMax_vcgc[2](7,19,17) << " " + << gradLogN_s[iSpecies](7,19,17) << " " + << temperature_scgc(7,19,17) << " " + << gradTemp(7,19,17) << " " + << diffVertVel_s[iSpecies](7,19,17) << " " + << v2or(7,19,17) << "\n"; + } */ } else { species[iSpecies].newVelocity_vcgc[2].zeros(); species[iSpecies].newDensity_scgc = species[iSpecies].density_scgc; } } - - - newTemperature_scgc = temperature_scgc - dt * (velocity_vcgc[2] % gradTemp + gmo % (temperature_scgc % divBulkVertVel)) + dt * diffTemp; +/* + if (iProc == 11) + std::cout << "temp : " + << temperature_scgc(7,19,17) << " " + << newTemperature_scgc(7,19,17) << " " + << velocity_vcgc[2](7,19,17) << " " + << gradTemp(7,19,17) << " " + << gmo(7,19,17) << " " + << divBulkVertVel(7,19,17) << " " + << diffTemp(7,19,17) << "\n"; +*/ + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) if (species[iSpecies].DoAdvect) - species[iSpecies].newVelocity_vcgc[2].clamp(-100,100); + species[iSpecies].newVelocity_vcgc[2].clamp(-150,150); newTemperature_scgc.clamp(10, 1e32); for (iX = nGCs; iX < nXs - nGCs; iX++) From 31bd855fd90292ae5b35813e260d2e400ff78881 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 13:59:55 -0500 Subject: [PATCH 064/127] FEAT: things for vertical advection --- include/ions.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/include/ions.h b/include/ions.h index e3f20266..bd8dd1ff 100644 --- a/include/ions.h +++ b/include/ions.h @@ -20,7 +20,7 @@ class Ions { std::string cName; precision_t mass; int charge; - + int vibe; int DoAdvect; std::vector nu_ion_neutral_coef; @@ -39,8 +39,10 @@ class Ions { // Sources and Losses: arma_cube density_scgc; + arma_cube newDensity_scgc; std::vector par_velocity_vcgc; std::vector perp_velocity_vcgc; + std::vector velocity_vcgc; arma_cube temperature_scgc; arma_cube conduction_scgc; @@ -59,6 +61,9 @@ class Ions { arma_cube electron_temperature_scgc; arma_cube rho_scgc; arma_cube mean_major_mass_scgc; + std::vector cMax_vcgc; + arma_cube sound_scgc; + arma_cube gamma_scgc; // This is the vector that will contain all of the different species: std::vector species; @@ -100,6 +105,12 @@ class Ions { void init_ion_temperature(Neutrals neutrals, Grid grid); void set_floor(); void fill_electrons(); + void calc_sound_speed(); + void calc_cMax(); + void set_bcs(Grid grid); + void set_upper_bcs(Grid grid); + void set_lower_bcs(Grid grid); + int get_species_id(std::string name); void calc_efield(Grid grid); void calc_exb_drift(Grid grid); @@ -113,5 +124,14 @@ class Ions { bool check_for_nonfinites(); void nan_test(std::string variable); bool restart_file(std::string dir, bool DoRead); + + /********************************************************************** + \brief Vertical advection solver - Rusanov + \param grid The grid to define the neutrals on + \param time contains information about the current time + **/ + + void solver_vertical_rusanov(Grid grid, Times time); + }; #endif // INCLUDE_IONS_H_ From 6f86379577b8427a1039158765bb925ef8231779 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:01:14 -0500 Subject: [PATCH 065/127] FEAT: calc_dt made generic --- include/neutrals.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/neutrals.h b/include/neutrals.h index e041e758..664e9714 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -361,14 +361,6 @@ class Neutrals { **/ void calc_cMax(); - /********************************************************************** - \brief Calculate dt (cell size / cMax) in each direction, and take min - \param dt returns the neutral time-step - \param grid The grid to define the neutrals on - **/ - precision_t calc_dt(Grid grid); - precision_t calc_dt_cubesphere(Grid grid); - /********************************************************************** \brief Calculate the chapman integrals for the individual species \param grid The grid to define the neutrals on From 4bb842caaa440787a15f01e7503191e918080d18 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:01:42 -0500 Subject: [PATCH 066/127] FEAT: calc_dt made generic --- include/solvers.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/solvers.h b/include/solvers.h index eaf16392..48515deb 100644 --- a/include/solvers.h +++ b/include/solvers.h @@ -104,4 +104,15 @@ precision_t limiter_mc(precision_t dUp, precision_t dDown, precision_t beta); + + /********************************************************************** + \brief Calculate dt (cell size / cMax) in each direction, and take min + \param dt returns the neutral time-step + \param grid The grid to define the neutrals on + **/ + precision_t calc_dt(Grid grid, std::vector cMax_vcgc); + precision_t calc_dt_sphere(Grid grid, std::vector cMax_vcgc); + precision_t calc_dt_cubesphere(Grid grid, std::vector cMax_vcgc); + precision_t calc_dt_vertical(Grid grid, std::vector cMax_vcgc); + #endif // INCLUDE_SOLVERS_H_ From f550c5e8f28cedb7c87201af0af17c28db4f0456 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:02:41 -0500 Subject: [PATCH 067/127] FEAT: ignore seconds in filename --- include/times.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/times.h b/include/times.h index f0fff47e..61277e9d 100644 --- a/include/times.h +++ b/include/times.h @@ -91,8 +91,10 @@ class Times { \brief Returns the current time as a string (year, month...) Returns the current time as a string of format YYYYMMDD_HHMMSS + + \param useSeconds if false, replace seconds with 0 **/ - std::string get_YMD_HMS(); + std::string get_YMD_HMS(bool useSeconds); /************************************************************** \brief Returns the intermediate time in seconds since ref date @@ -226,6 +228,9 @@ class Times { /// represented as YYYYMMDD_HHMMSS std::string sYMD_HMS; + /// represented as YYYYMMDD_HHMM00 + std::string sYMD_HM0; + // ------------------------------------------------------------- // Keeping track of walltime for the run: From 1df8f066bff0c17ee7976c59c41ccc042e92e044 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:03:19 -0500 Subject: [PATCH 068/127] FEAT: include vibration states --- share/run/UA/inputs/earth.in | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/share/run/UA/inputs/earth.in b/share/run/UA/inputs/earth.in index e8f4432b..a77b5672 100644 --- a/share/run/UA/inputs/earth.in +++ b/share/run/UA/inputs/earth.in @@ -57,12 +57,12 @@ O+ is O+(4S) is the base state of O+, but is just called O+ O+ has excited states of O+(2D) and O+(2P). #IONS -name, mass, charge, advect -O+, 16, 1, 1 -O2+, 32, 1, 0 -N2+, 28, 1, 0 -NO+, 30, 1, 0 -N+, 14, 1, 0 -He+, 4, 1, 1 -O+_2D, 16, 1, 0 -O+_2P, 16, 1, 0 +name, mass, charge, vibration, advect +O+, 16, 1, 5, 1 +O2+, 32, 1, 7, 0 +N2+, 28, 1, 7, 0 +NO+, 30, 1, 7, 0 +N+, 14, 1, 5, 0 +He+, 4, 1, 5, 1 +O+_2D, 16, 1, 5, 0 +O+_2P, 16, 1, 5, 0 From 53b928875ecd15fea631fbcf684b93b0c8138df3 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:04:32 -0500 Subject: [PATCH 069/127] FEAT: enable ion advection --- src/advance.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index c5fe05c4..6e8b0e21 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -49,8 +49,12 @@ bool advance(Planets &planet, neutrals.calc_viscosity(); neutrals.calc_cMax(); - precision_t dtNeutral = neutrals.calc_dt(gGrid); - precision_t dtIon = 100.0; + ions.fill_electrons(); + ions.calc_sound_speed(); + ions.calc_cMax(); + + precision_t dtNeutral = calc_dt(gGrid, neutrals.cMax_vcgc); + precision_t dtIon = calc_dt(gGrid, ions.cMax_vcgc); time.calc_dt(dtNeutral, dtIon); // ------------------------------------ @@ -63,7 +67,10 @@ bool advance(Planets &planet, if (didWork) didWork = neutrals.set_bcs(gGrid, time, indices); - + + if (didWork) + didWork = ions.set_bcs(gGrid, time, indices); + neutrals.exchange_old(gGrid); advect(gGrid, time, neutrals); From dbae1e847e9755cf8918c042f8d91341490c5aeb Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:05:03 -0500 Subject: [PATCH 070/127] STY: formatting --- src/calc_ion_drift.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calc_ion_drift.cpp b/src/calc_ion_drift.cpp index 93a139d0..76c7d51d 100644 --- a/src/calc_ion_drift.cpp +++ b/src/calc_ion_drift.cpp @@ -198,9 +198,11 @@ void Ions::calc_ion_drift(Neutrals neutrals, sum_rho = sum_rho + rho; for (int64_t iComp = 0; iComp < 3; iComp++) { + species[iIon].velocity_vcgc[iComp] = + species[iIon].perp_velocity_vcgc[iComp] + + species[iIon].par_velocity_vcgc[iComp]; velocity_vcgc[iComp] = velocity_vcgc[iComp] + - rho % (species[iIon].perp_velocity_vcgc[iComp] + - species[iIon].par_velocity_vcgc[iComp]); + rho % (species[iIon].velocity_vcgc[iComp]); } } // if DoAdvect From b42746bc9dfb55884c3a178d46305b6b0e323864 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:06:26 -0500 Subject: [PATCH 071/127] FEAT: enable ion advection --- src/ions.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/ions.cpp b/src/ions.cpp index 492d820f..ca11e8d3 100644 --- a/src/ions.cpp +++ b/src/ions.cpp @@ -20,6 +20,7 @@ Ions::species_chars Ions::create_species(Grid grid) { // Constants: tmp.DoAdvect = 0; + tmp.vibe = 5; tmp.density_scgc.set_size(nLons, nLats, nAlts); tmp.density_scgc.fill(1e10); @@ -35,10 +36,12 @@ Ions::species_chars Ions::create_species(Grid grid) { tmp.par_velocity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); tmp.perp_velocity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); + tmp.velocity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); for (int iDir = 0; iDir < 3; iDir++) { tmp.par_velocity_vcgc[iDir].zeros(); tmp.perp_velocity_vcgc[iDir].zeros(); + tmp.velocity_vcgc[iDir].zeros(); } // The collision frequencies need the neutrals, so those are @@ -87,9 +90,17 @@ Ions::Ions(Grid grid, Planets planet) { density_scgc.set_size(nLons, nLats, nAlts); density_scgc.ones(); velocity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); + cMax_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); - for (int iDir = 0; iDir < 3; iDir++) + gamma_scgc.set_size(nLons, nLats, nAlts); + gamma_scgc.ones(); + sound_scgc.set_size(nLons, nLats, nAlts); + sound_scgc.ones(); + + for (int iDir = 0; iDir < 3; iDir++) { velocity_vcgc[iDir].zeros(); + cMax_vcgc[iDir].ones(); + } temperature_scgc.set_size(nLons, nLats, nAlts); temperature_scgc.fill(200.0); @@ -156,6 +167,7 @@ int Ions::read_planet_file(Planets planet) { species[iSpecies].mass = mass * cAMU; species[iSpecies].charge = ions["charge"][iSpecies]; species[iSpecies].DoAdvect = ions["advect"][iSpecies]; + species[iSpecies].vibe = ions["vibration"][iSpecies]; } // account for advected ions: @@ -238,6 +250,78 @@ void Ions::set_floor() { return; } +// ---------------------------------------------------------------------- +// Calculate a bunch of derived products: +// - Gamma +// - Speed of sound +// ---------------------------------------------------------------------- + +void Ions::calc_sound_speed() { + + int64_t iSpecies; + + std::string function = "Ion::calc_sound_speed"; + static int iFunction = -1; + report.enter(function, iFunction); + + gamma_scgc.zeros(); + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + gamma_scgc = gamma_scgc + + species[iSpecies].density_scgc / (species[iSpecies].vibe - 2); + } + gamma_scgc = gamma_scgc * 2.0 / density_scgc + 1.0; + + sound_scgc = sqrt(cKB * + gamma_scgc % + temperature_scgc / + mean_major_mass_scgc); + + if (report.test_verbose(2)) { + std::cout << "max sound speed : " << sound_scgc.max() << "\n"; + std::cout << "max gamma : " << gamma_scgc.max() << "\n"; + if (!all_finite(sound_scgc, "sound speed")) { + std::cout << "sound speed has nans!\n"; + report.report_errors(); + } + } + + report.exit(function); + return; +} + +// ---------------------------------------------------------------------- +// Calculate cMax, which is the sound speed + velocity in each +// direction +// ---------------------------------------------------------------------- + +void Ions::calc_cMax() { + + std::string function = "Ions::calc_cMax"; + static int iFunction = -1; + report.enter(function, iFunction); + + int iDir; + + // just take the bulk value for now: + + if (report.test_verbose(3)) { + std::cout << "max sound speed : " << sound_scgc.max() << "\n"; + + for (iDir = 0; iDir < 3; iDir++) { + arma_cube tmp = abs(velocity_vcgc[iDir]); + std::cout << "min velocity : " << tmp.min() << "\n"; + std::cout << "max velocity : " << tmp.max() << "\n"; + } + } + + for (iDir = 0; iDir < 3; iDir++) + cMax_vcgc[iDir] = sound_scgc + abs(velocity_vcgc[iDir]); + + report.exit(function); + return; +} + + // ----------------------------------------------------------------------------- // Calculate the electron density from the sum of all ion species // ----------------------------------------------------------------------------- From ee834257242ac3c68a54a7437b719918ecc957d2 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:06:58 -0500 Subject: [PATCH 072/127] FEAT: ignore seconds in filename --- src/output.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/output.cpp b/src/output.cpp index 66ca3704..d30416b4 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -312,7 +312,10 @@ bool output(const Neutrals &neutrals, if (type_output == "therm") filename = "3DTHR_"; - filename = filename + time.get_YMD_HMS(); + bool useSeconds = false; + if (input.get_dt_output(iOutput) < 60) + useSeconds = true; + filename = filename + time.get_YMD_HMS(useSeconds); if (nMembers > 1) filename = filename + "_" + cMember; From 7b251b10240a00c72ca70c3b63f1bbbb70730b79 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:07:27 -0500 Subject: [PATCH 073/127] FEAT: ignore seconds in filename --- src/time.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/time.cpp b/src/time.cpp index d0962e33..385c29ff 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -139,8 +139,11 @@ double Times::get_end() { // Get the current time as a string // ----------------------------------------------------------------------------- -std::string Times::get_YMD_HMS() { - return sYMD_HMS; +std::string Times::get_YMD_HMS(bool useSeconds) { + if (useSeconds) + return sYMD_HMS; + else + return sYMD_HM0; } // ----------------------------------------------------------------------------- @@ -230,6 +233,9 @@ void Times::increment_time() { sprintf(tmp, "%04d%02d%02d_%02d%02d%02d", year, month, day, hour, minute, second); sYMD_HMS = std::string(tmp); + sprintf(tmp, "%04d%02d%02d_%02d%02d%02d", + year, month, day, hour, minute, 0); + sYMD_HM0 = std::string(tmp); sprintf(tmp, "%04d%02d%02d", year, month, day); sYMD = std::string(tmp); sprintf(tmp, "%02d%02d%02d", hour, minute, second); From 0dcca19e459ffbc81b77f4199d52a0f323dbd44c Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:08:35 -0500 Subject: [PATCH 074/127] STY: astyle within vscode... --- src/main/main.cpp | 412 +++++++++++++++++++++++----------------------- 1 file changed, 206 insertions(+), 206 deletions(-) diff --git a/src/main/main.cpp b/src/main/main.cpp index 9e5d2ddb..1233b103 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -10,214 +10,214 @@ int main() { - int iErr = 0; - std::string sError; - bool didWork = true; - - Times time; - - // Define the function and report: - std::string function = "main"; - static int iFunction = -1; - report.enter(function, iFunction); - - try { - // Create inputs (reading the input file): - input = Inputs(time); - if (!input.is_ok()) - throw std::string("input initialization failed!"); - - if (input.get_is_student()) - report.print(-1, "Hello " + - input.get_student_name() + " - welcome to Aether!"); - - Quadtree quadtree; - if (!quadtree.is_ok()) - throw std::string("quadtree initialization failed!"); - - // Initialize MPI and parallel aspects of the code: - didWork = init_parallel(quadtree); - if (!didWork) - throw std::string("init_parallel failed!"); - - // Everything should be set for the inputs now, so write a restart file: - didWork = input.write_restart(); - if (!didWork) - throw std::string("input.write_restart failed!"); - - // Initialize the EUV system: - Euv euv; - if (!euv.is_ok()) - throw std::string("EUV initialization failed!"); - - // Initialize the planet: - Planets planet; - MPI_Barrier(aether_comm); - if (!planet.is_ok()) - throw std::string("planet initialization failed!"); - - // Initialize the indices, read the files, and perturb: - Indices indices; - didWork = read_and_store_indices(indices); - MPI_Barrier(aether_comm); - if (!didWork) - throw std::string("read_and_store_indices failed!"); - - // Perturb the inputs if user has asked for this - indices.perturb(); - MPI_Barrier(aether_comm); - - // Initialize Geographic grid: - Grid gGrid(input.get_nLonsGeo(), - input.get_nLatsGeo(), - input.get_nAltsGeo(), - nGeoGhosts); - didWork = gGrid.init_geo_grid(quadtree, planet); - MPI_Barrier(aether_comm); - if (!didWork) - throw std::string("init_geo_grid failed!"); - - // Calculate centripetal acceleration, since this is a constant - // vector on the grid: - if (input.get_use_centripetal()) - gGrid.calc_cent_acc(planet); - - // Initialize Magnetic grid: - Grid mGrid(nMagLonsG, nMagLatsG, nMagAltsG, nMagGhosts); - - // Initialize Neutrals on geographic grid: - Neutrals neutrals(gGrid, planet, time, indices); - - // Initialize Ions on geographic grid: - Ions ions(gGrid, planet); - - // ----------------------------------------------------------------- - // This is a unit test for checking for nans and infinities. - // Is simply adds nans and infinities in a few places, then - // checks for them to make sure the checking is working - // ----------------------------------------------------------------- - - if (input.get_nan_test()) { - neutrals.nan_test(input.get_nan_test_variable()); - ions.nan_test(input.get_nan_test_variable()); + int iErr = 0; + std::string sError; + bool didWork = true; + + Times time; + + // Define the function and report: + std::string function = "main"; + static int iFunction = -1; + report.enter(function, iFunction); + + try { + // Create inputs (reading the input file): + input = Inputs(time); + if (!input.is_ok()) + throw std::string("input initialization failed!"); + + if (input.get_is_student()) + report.print(-1, "Hello " + + input.get_student_name() + " - welcome to Aether!"); + + Quadtree quadtree; + if (!quadtree.is_ok()) + throw std::string("quadtree initialization failed!"); + + // Initialize MPI and parallel aspects of the code: + didWork = init_parallel(quadtree); + if (!didWork) + throw std::string("init_parallel failed!"); + + // Everything should be set for the inputs now, so write a restart file: + didWork = input.write_restart(); + if (!didWork) + throw std::string("input.write_restart failed!"); + + // Initialize the EUV system: + Euv euv; + if (!euv.is_ok()) + throw std::string("EUV initialization failed!"); + + // Initialize the planet: + Planets planet; + MPI_Barrier(aether_comm); + if (!planet.is_ok()) + throw std::string("planet initialization failed!"); + + // Initialize the indices, read the files, and perturb: + Indices indices; + didWork = read_and_store_indices(indices); + MPI_Barrier(aether_comm); + if (!didWork) + throw std::string("read_and_store_indices failed!"); + + // Perturb the inputs if user has asked for this + indices.perturb(); + MPI_Barrier(aether_comm); + + // Initialize Geographic grid: + Grid gGrid(input.get_nLonsGeo(), + input.get_nLatsGeo(), + input.get_nAltsGeo(), + nGeoGhosts); + didWork = gGrid.init_geo_grid(quadtree, planet); + MPI_Barrier(aether_comm); + if (!didWork) + throw std::string("init_geo_grid failed!"); + + // Calculate centripetal acceleration, since this is a constant + // vector on the grid: + if (input.get_use_centripetal()) + gGrid.calc_cent_acc(planet); + + // Initialize Magnetic grid: + Grid mGrid(nMagLonsG, nMagLatsG, nMagAltsG, nMagGhosts); + + // Initialize Neutrals on geographic grid: + Neutrals neutrals(gGrid, planet, time, indices); + + // Initialize Ions on geographic grid: + Ions ions(gGrid, planet); + + // ----------------------------------------------------------------- + // This is a unit test for checking for nans and infinities. + // Is simply adds nans and infinities in a few places, then + // checks for them to make sure the checking is working + // ----------------------------------------------------------------- + + if (input.get_nan_test()) { + neutrals.nan_test(input.get_nan_test_variable()); + ions.nan_test(input.get_nan_test_variable()); + } + + if (input.get_check_for_nans()) { + didWork = neutrals.check_for_nonfinites("In Main, before main loop"); + didWork = ions.check_for_nonfinites(); + } + + // ----------------------------------------------------------------- + + // Once EUV, neutrals, and ions have been defined, pair cross sections + euv.pair_euv(neutrals, ions); + + // Initialize Chemical scheme (including reading file): + Chemistry chemistry(neutrals, ions); + + // Read in the collision frequencies and other diffusion coefficients: + read_collision_file(neutrals, ions); + + // Initialize ion temperatures from neutral temperature + ions.init_ion_temperature(neutrals, gGrid); + + // Initialize electrodynamics and check if electrodynamics times + // works with input time + Electrodynamics electrodynamics(time); + if (!electrodynamics.is_ok()) + throw std::string("electrodynamics initialization failed!"); + + // If the user wants to restart, then get the time of the restart + if (input.get_do_restart()) { + report.print(1, "Restarting! Reading time file!"); + didWork = time.restart_file(input.get_restartin_dir(), DoRead); + if (!didWork) + throw std::string("Reading Restart for time Failed!!!\n"); + } + + // This is for the initial output. If it is not a restart, this will go: + if (time.check_time_gate(input.get_dt_output(0))) + didWork = output(neutrals, ions, gGrid, time, planet); + if (!didWork) + throw std::string("output failed!"); + + // This is advancing now... We are not coupling, so set dt_couple to the + // end of the simulation + + double dt_couple = time.get_end() - time.get_current(); + + // The way most codes are set up in the SWMF is that there are two + // times, an end time which ends the simulation, and an intermediate + // time, which allows coupling or something to happen. So, typically + // the advance functions should only go to this intermediate time, + // then a loop around that goes to the end time. Then, the code can + // be made into a library and run externally. + + Logfile logfile(indices); + while (time.get_current() < time.get_end()) { + + time.increment_intermediate(dt_couple); + + // Increment until the intermediate time: + while (time.get_current() < time.get_intermediate()) { + didWork = advance(planet, + gGrid, + time, + euv, + neutrals, + ions, + chemistry, + electrodynamics, + indices, + logfile); + if (!didWork) + throw std::string("Error in advance!"); + } + + // Should write out some restart files every time we are done with + // intermediate times. Just so when we restart, we know that we can + // couple first thing and everything should be good. (Not sure if + // restart should be before or after the coupling, but since we are + // not coupling, it doesn't matter. Once we do coupling to something, + // need to figure it out. + // + // The odd thing here is that in advance, we most likely JUST + // wrote out restart files, so we only need to do this if we + // didn't just do it. So, check the negative here: + if (!time.check_time_gate(input.get_dt_write_restarts())) { + report.print(3, "Writing restart files"); + + didWork = neutrals.restart_file(input.get_restartout_dir(), DoWrite); + if (!didWork) + throw std::string("Writing Restart for Neutrals Failed!!!\n"); + + didWork = ions.restart_file(input.get_restartout_dir(), DoWrite); + if (!didWork) + throw std::string("Writing Restart for Ions Failed!!!\n"); + + didWork = time.restart_file(input.get_restartout_dir(), DoWrite); + if (!didWork) + throw std::string("Writing Restart for time Failed!!!\n"); + } + + // Do some coupling here. But we have no coupling to do. Sad. + + } // End of outer time loop - done with run! + + report.exit(function); + report.times(); + + } catch (std::string error) { + report.report_errors(); + if (iProc == 0) { + std::cout << error << "\n"; + std::cout << "---- Must Exit! ----\n"; + } } - if (input.get_check_for_nans()) { - didWork = neutrals.check_for_nonfinites("In Main, before main loop"); - didWork = ions.check_for_nonfinites(); - } - - // ----------------------------------------------------------------- - - // Once EUV, neutrals, and ions have been defined, pair cross sections - euv.pair_euv(neutrals, ions); - - // Initialize Chemical scheme (including reading file): - Chemistry chemistry(neutrals, ions); - - // Read in the collision frequencies and other diffusion coefficients: - read_collision_file(neutrals, ions); - - // Initialize ion temperatures from neutral temperature - ions.init_ion_temperature(neutrals, gGrid); - - // Initialize electrodynamics and check if electrodynamics times - // works with input time - Electrodynamics electrodynamics(time); - if (!electrodynamics.is_ok()) - throw std::string("electrodynamics initialization failed!"); - - // If the user wants to restart, then get the time of the restart - if (input.get_do_restart()) { - report.print(1, "Restarting! Reading time file!"); - didWork = time.restart_file(input.get_restartin_dir(), DoRead); - if (!didWork) - throw std::string("Reading Restart for time Failed!!!\n"); - } - - // This is for the initial output. If it is not a restart, this will go: - if (time.check_time_gate(input.get_dt_output(0))) - didWork = output(neutrals, ions, gGrid, time, planet); - if (!didWork) - throw std::string("output failed!"); - - // This is advancing now... We are not coupling, so set dt_couple to the - // end of the simulation - - double dt_couple = time.get_end() - time.get_current(); - - // The way most codes are set up in the SWMF is that there are two - // times, an end time which ends the simulation, and an intermediate - // time, which allows coupling or something to happen. So, typically - // the advance functions should only go to this intermediate time, - // then a loop around that goes to the end time. Then, the code can - // be made into a library and run externally. - - Logfile logfile(indices); - while (time.get_current() < time.get_end()) { - - time.increment_intermediate(dt_couple); - - // Increment until the intermediate time: - while (time.get_current() < time.get_intermediate()) { - didWork = advance(planet, - gGrid, - time, - euv, - neutrals, - ions, - chemistry, - electrodynamics, - indices, - logfile); - if (!didWork) - throw std::string("Error in advance!"); - } - - // Should write out some restart files every time we are done with - // intermediate times. Just so when we restart, we know that we can - // couple first thing and everything should be good. (Not sure if - // restart should be before or after the coupling, but since we are - // not coupling, it doesn't matter. Once we do coupling to something, - // need to figure it out. - // - // The odd thing here is that in advance, we most likely JUST - // wrote out restart files, so we only need to do this if we - // didn't just do it. So, check the negative here: - if (!time.check_time_gate(input.get_dt_write_restarts())) { - report.print(3, "Writing restart files"); - - didWork = neutrals.restart_file(input.get_restartout_dir(), DoWrite); - if (!didWork) - throw std::string("Writing Restart for Neutrals Failed!!!\n"); - - didWork = ions.restart_file(input.get_restartout_dir(), DoWrite); - if (!didWork) - throw std::string("Writing Restart for Ions Failed!!!\n"); - - didWork = time.restart_file(input.get_restartout_dir(), DoWrite); - if (!didWork) - throw std::string("Writing Restart for time Failed!!!\n"); - } - - // Do some coupling here. But we have no coupling to do. Sad. - - } // End of outer time loop - done with run! - - report.exit(function); - report.times(); - - } catch (std::string error) { - report.report_errors(); - if (iProc == 0) { - std::cout << error << "\n"; - std::cout << "---- Must Exit! ----\n"; - } - } - - // End parallel tasks: - iErr = MPI_Finalize(); + // End parallel tasks: + iErr = MPI_Finalize(); - return iErr; + return iErr; } From 30a80162eb20e0f67bb3bdb64413ee5a927404d1 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:09:47 -0500 Subject: [PATCH 075/127] FEAT: vertical ion advection --- src/solver_vertical_rusanov.cpp | 98 +++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/solver_vertical_rusanov.cpp b/src/solver_vertical_rusanov.cpp index 74b8814c..b59baf88 100644 --- a/src/solver_vertical_rusanov.cpp +++ b/src/solver_vertical_rusanov.cpp @@ -435,3 +435,101 @@ void Neutrals::solver_vertical_rusanov(Grid grid, report.exit(function); return; } + + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + +void Ions::solver_vertical_rusanov(Grid grid, + Times time) { + + std::string function = "Ions::solver_vertical_rusanov"; + static int iFunction = -1; + report.enter(function, iFunction); + + int64_t nXs = grid.get_nX(), iX; + int64_t nYs = grid.get_nY(), iY; + int64_t nZs = grid.get_nZ(), iZ; + int64_t nGCs = grid.get_nGCs(); + int iDir, iSpecies; + + precision_t dt = time.get_dt(); + + // ----------------------------------------------------------- + // Bulk Variables: + std::vector gradVel, diffVel; + gradVel = make_cube_vector(nXs, nYs, nZs, 3); + diffVel = make_cube_vector(nXs, nYs, nZs, 3); + + arma_cube gradDummy(nXs, nYs, nZs), diffDummy(nXs, nYs, nZs); + + // ----------------------------------------------------------- + // species dependent variables: + std::vector gradLogN_s, diffLogN_s; + std::vector gradVertVel_s, diffVertVel_s, divVertVel_s; + gradLogN_s = make_cube_vector(nXs, nYs, nZs, nSpecies); + diffLogN_s = make_cube_vector(nXs, nYs, nZs, nSpecies); + gradVertVel_s = make_cube_vector(nXs, nYs, nZs, nSpecies); + diffVertVel_s = make_cube_vector(nXs, nYs, nZs, nSpecies); + divVertVel_s = make_cube_vector(nXs, nYs, nZs, nSpecies); + + arma_cube log_s(nXs, nYs, nZs), vv_s(nXs, nYs, nZs); + + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + if (species[iSpecies].DoAdvect) { + + // Log(number density): + log_s = log(species[iSpecies].density_scgc); + + calc_grad_and_diff_alts_rusanov(grid, + log_s, + cMax_vcgc[2], + gradDummy, + diffDummy); + + gradLogN_s[iSpecies] = gradDummy; + diffLogN_s[iSpecies] = diffDummy; + + // Vertical Velocity for each species: + vv_s = species[iSpecies].velocity_vcgc[2]; + calc_grad_and_diff_alts_rusanov(grid, + vv_s, + cMax_vcgc[2], + gradDummy, + diffDummy); + gradVertVel_s[iSpecies] = gradDummy; + diffVertVel_s[iSpecies] = diffDummy; + divVertVel_s[iSpecies] = gradDummy + 2 * vv_s / grid.radius_scgc; + } else { + gradVertVel_s[iSpecies].zeros(); + diffVertVel_s[iSpecies].zeros(); + divVertVel_s[iSpecies].zeros(); + } + } + + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + if (species[iSpecies].DoAdvect) { + + // densities: + log_s = + log(species[iSpecies].density_scgc) + - dt * (divVertVel_s[iSpecies] + + species[iSpecies].velocity_vcgc[2] % gradLogN_s[iSpecies]) + + dt * diffLogN_s[iSpecies]; + species[iSpecies].newDensity_scgc = exp(log_s); + + } else { + species[iSpecies].newDensity_scgc = species[iSpecies].density_scgc; + } + } + + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) + if (species[iSpecies].DoAdvect) + species[iSpecies].density_scgc = species[iSpecies].newDensity_scgc; + + fill_electrons(); + + report.exit(function); + return; +} From c53f5048742754b60034bdf9b5bfd78bbaa08c26 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:10:32 -0500 Subject: [PATCH 076/127] FEAT: make calc_dt generic so ions can use --- src/calc_dt.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/calc_dt.cpp diff --git a/src/calc_dt.cpp b/src/calc_dt.cpp new file mode 100644 index 00000000..5854eadf --- /dev/null +++ b/src/calc_dt.cpp @@ -0,0 +1,149 @@ +// Copyright 2020, the Aether Development Team (see doc/dev_team.md for members) +// Full license can be found in License.md + +#include "../include/aether.h" + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + + +precision_t calc_dt(Grid grid, std::vector cMax_vcgc) { + + std::string function = "calc_dt"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + + if (input.get_is_cubesphere()) + dt = calc_dt_cubesphere(grid, cMax_vcgc); + else + dt = calc_dt_sphere(grid, cMax_vcgc); + + report.exit(function); + return dt; +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + +precision_t calc_dt_sphere(Grid grid, std::vector cMax_vcgc) { + + std::string function = "calc_dt_sphere"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + + arma_vec dta(4); + arma_cube dtCube; + + // Longitudinal Direction: + dtCube = grid.dlon_center_dist_scgc / cMax_vcgc[0]; + dta(0) = dtCube.min(); + + // Latitudinal Direction: + dtCube = grid.dlat_center_dist_scgc / cMax_vcgc[1]; + dta(1) = dtCube.min(); + + // Vertical Direction: + dta(2) = calc_dt_vertical(grid, cMax_vcgc); + + // Set a minimum dt: + dta(3) = 10.0; + + dt = dta.min(); + + if (report.test_verbose(3)) + std::cout << "dt (sphere) for neutrals : " << dt << "\n"; + + if (report.test_verbose(4)) + std::cout << " derived from dt(x, y, z, extra) : " << dta << "\n"; + + report.exit(function); + return dt; +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + +precision_t calc_dt_cubesphere(Grid grid, std::vector cMax_vcgc) { + + std::string function = "calc_dt_sphere"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + arma_vec dta(4); + + // Get some dimensions + int64_t nAlts = grid.get_nAlts(); + int64_t nXs = grid.get_nLons(); + int64_t nYs = grid.get_nLats(); + + // dtx dty for reference coordinate system + arma_cube dtx(nXs, nYs, nAlts); + arma_cube dty(nXs, nYs, nAlts); + + // A dummy constant one matrix + arma_mat dummy_1(nXs, nYs, fill::ones); + + // Loop through altitudes + for (int iAlt = 0; iAlt < nAlts; iAlt++) { + // Conver cMax to contravariant velocity first + arma_mat u1 = sqrt( + cMax_vcgc[0].slice(iAlt) % grid.A11_inv_scgc.slice(iAlt) % + cMax_vcgc[0].slice(iAlt) % grid.A11_inv_scgc.slice(iAlt) + + cMax_vcgc[1].slice(iAlt) % grid.A12_inv_scgc.slice(iAlt) % + cMax_vcgc[1].slice(iAlt) % grid.A12_inv_scgc.slice(iAlt)); + arma_mat u2 = sqrt( + cMax_vcgc[0].slice(iAlt) % grid.A21_inv_scgc.slice(iAlt) % + cMax_vcgc[0].slice(iAlt) % grid.A21_inv_scgc.slice(iAlt) + + cMax_vcgc[1].slice(iAlt) % grid.A22_inv_scgc.slice(iAlt) % + cMax_vcgc[1].slice(iAlt) % grid.A22_inv_scgc.slice(iAlt)); + dtx.slice(iAlt) = grid.drefx(iAlt) * dummy_1 / u1; + dty.slice(iAlt) = grid.drefy(iAlt) * dummy_1 / u2; + } + // Take minimum dts in each direction: + dta(0) = dtx.min(); + dta(1) = dty.min(); + // Vertical Direction: + dta(2) = calc_dt_vertical(grid, cMax_vcgc); + // Set a minimum dt: + dta(3) = 10.0; + // Take the minimum of all directions: + dt = dta.min(); + + if (report.test_verbose(3)) + std::cout << "dt (cubesphere) : " << dt << "\n"; + + if (report.test_verbose(4)) + std::cout << " derived from dt(x, y, z, extra) : " << dta << "\n"; + + report.exit(function); + return dt; +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- + +precision_t calc_dt_vertical(Grid grid, std::vector cMax_vcgc) { + + std::string function = "calc_dt_vertical"; + static int iFunction = -1; + report.enter(function, iFunction); + + precision_t dt; + if (input.get_nAltsGeo() > 1) { + arma_cube dtz = grid.dalt_center_scgc / cMax_vcgc[2]; + dt = dtz.min(); + } else + dt = 1e32; + + report.exit(function); + return dt; +} \ No newline at end of file From 9477fd70fdb92e9d2b4e2fd38df6e08112cd35bc Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 29 Dec 2023 14:10:53 -0500 Subject: [PATCH 077/127] FEAT: set ion BCs --- src/ions_bcs.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/ions_bcs.cpp diff --git a/src/ions_bcs.cpp b/src/ions_bcs.cpp new file mode 100644 index 00000000..bc423412 --- /dev/null +++ b/src/ions_bcs.cpp @@ -0,0 +1,135 @@ +// Copyright 2023, the Aether Development Team (see doc/dev_team.md for members) +// Full license can be found in License.md +// +// initial version - A. Ridley - May 27, 2023 + +#include "aether.h" + +// ----------------------------------------------------------------------------- +// Set initial conditions for the neutrals. +// Two methods implemented so far: +// - Planet: Use fixed density values in the planet.in file and the +// temperature profile to set the densities and temperature. +// Densities are filled with hydrostatic solution. +// - Msis: Use NRL MSIS to set the densities and temperatures. If the +// densities are not found, then set to density in planet.in +// file and fill with hydrostatic. +// ----------------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// set_bcs - This is for setting the vertical BCs +//---------------------------------------------------------------------- + +bool Ions::set_bcs(Grid grid, + Times time, + Indices indices) { + + std::string function = "Ions::set_bcs"; + static int iFunction = -1; + report.enter(function, iFunction); + + bool didWork = true; + + if (input.get_nAltsGeo() > 1) { + didWork = set_lower_bcs(grid, time, indices); + + if (didWork) + didWork = set_upper_bcs(grid); + + if (didWork) + fill_electrons(); + } + + if (!didWork) + report.error("issue with ion BCs!"); + + report.exit(function); + return didWork; +} + +//---------------------------------------------------------------------- +// set upper boundary conditions for the ions +//---------------------------------------------------------------------- + +bool Ions::set_upper_bcs(Grid grid) { + + std::string function = "Ions::set_upper_bcs"; + static int iFunction = -1; + report.enter(function, iFunction); + + bool didWork = true; + + int64_t nAlts = grid.get_nZ(); + int64_t nX = grid.get_nX(), iX; + int64_t nY = grid.get_nY(), iY; + int64_t nGCs = grid.get_nGCs(); + int64_t iAlt; + arma_mat h; + arma_mat aveT; + + for (iAlt = nAlts - nGCs; iAlt < nAlts; iAlt++) { + // Bulk Quantities: + temperature_scgc.slice(iAlt) = temperature_scgc.slice(iAlt - 1); + + // For each species: + for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + species[iSpecies].temperature_scgc.slice(iAlt) = + species[iSpecies].temperature_scgc.slice(iAlt - 1); + + aveT = (species[iSpecies].temperature_scgc.slice(iAlt) + + electon_temperature_scgc.slice(iAlt)); + // Calculate scale height for the species: + h = cKB * species[iSpecies].temperature_scgc.slice(iAlt) / + (species[iSpecies].mass % abs(grid.gravity_vcgc[2])); + // Assume each species falls of with (modified) hydrostatic: + species[iSpecies].density_scgc.slice(iAlt) = + species[iSpecies].density_scgc.slice(iAlt - 1) % + exp(-grid.dalt_lower_scgc.slice(iAlt) / h); + } + } + + report.exit(function); + return didWork; +} + +//---------------------------------------------------------------------- +// set lower boundary conditions for the ions +//---------------------------------------------------------------------- + +bool Ions::set_lower_bcs(Grid grid) { + + std::string function = "Ions::set_lower_bcs"; + static int iFunction = -1; + report.enter(function, iFunction); + + bool didWork = true; + + int64_t nAlts = grid.get_nZ(); + int64_t nX = grid.get_nX(), iX; + int64_t nY = grid.get_nY(), iY; + int64_t nGCs = grid.get_nGCs(); + int64_t iAlt; + arma_mat h; + arma_mat aveT; + + for (iAlt = nGCs - 1; iAlt >= 0; iAlt--) { + // Bulk Quantities: + temperature_scgc.slice(iAlt) = temperature_scgc.slice(iAlt + 1); + + // For each species: + for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + // assign all species temperatures the bulk temperature: + species[iSpecies].temperature_scgc.slice(iAlt) = + temperature_scgc.slice(iAlt); + // Assume each species falls off a bit. + // this BC shouldn't matter, since the bottom of the code + // should be in chemical equalibrium: + species[iSpecies].density_scgc.slice(iAlt) = + 0.95 * species[iSpecies].density_scgc.slice(iAlt + 1); + } + } + + report.exit(function); + return didWork; +} + From f2091ffc5779c5452b6b552372550f80a99e0356 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Sat, 20 Apr 2024 13:37:55 -0400 Subject: [PATCH 078/127] BUG: ignore partial lines --- srcPython/plot_logfiles.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/srcPython/plot_logfiles.py b/srcPython/plot_logfiles.py index 5483b90a..82eb40be 100755 --- a/srcPython/plot_logfiles.py +++ b/srcPython/plot_logfiles.py @@ -64,21 +64,23 @@ def read_ssv_file(file): while (iLine < iEnd): aline = lines[iLine].split(' ') - year = int(aline[0]) - month = int(aline[1]) - day = int(aline[2]) - hour = int(aline[3]) - minute = int(aline[4]) - second = int(aline[5]) - if (iOff == 6): - milli = 0 - else: - milli = int(aline[6]) - t = dt.datetime(year, month, day, hour, minute, second, milli) + if ((len(aline) - iOff) == nVars): + + year = int(aline[0]) + month = int(aline[1]) + day = int(aline[2]) + hour = int(aline[3]) + minute = int(aline[4]) + second = int(aline[5]) + if (iOff == 6): + milli = 0 + else: + milli = int(aline[6]) + t = dt.datetime(year, month, day, hour, minute, second, milli) - data["times"].append(t) - for iVar in range(nVars): - allValues[iVar, iLine - iStart] = float(aline[iOff + iVar]) + data["times"].append(t) + for iVar in range(nVars): + allValues[iVar, iLine - iStart] = float(aline[iOff + iVar]) iLine += 1 data['values'] = allValues @@ -317,6 +319,7 @@ def assign_file_to_color(file): print("Reading file : ",file) #data = read_timeline_file(file) data = read_ssv_file(file) + nT = len(data["times"])-1 for iVar in args.vars: label = data["vars"][iVar] color = 'black' @@ -326,7 +329,7 @@ def assign_file_to_color(file): if (nVars > 1): color,line,label = assign_var_to_color(data['vars'][iVar]) - ax.plot(data["times"], data["values"][iVar], label = label, + ax.plot(data["times"][:nT], data["values"][iVar,:nT], label = label, color = color, linestyle = line, linewidth = 2.0) iVar = args.vars[0] From e032527daf8eb65c6ebef14c55bafc798ed6c02e Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:08:03 -0400 Subject: [PATCH 079/127] FEAT: 1-sided 3rd order gradient --- include/grid.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/grid.h b/include/grid.h index fcee805e..e59a4c3c 100644 --- a/include/grid.h +++ b/include/grid.h @@ -114,6 +114,14 @@ class Grid { arma_cube MeshCoefp1; arma_cube MeshCoefp2; + // This is for a one-sided 3rd order gradient for the bottom boundary: + + arma_cube MeshCoef1s3rdp1; + arma_cube MeshCoef1s3rdp2; + arma_cube MeshCoef1s3rdp3; + arma_cube MeshCoef1s3rdp4; + arma_cube MeshCoef1s3rdp5; + arma_cube dlon_center_scgc; arma_cube dlon_center_dist_scgc; From 625777789e7dd7c83cec84743ef12f2e75d711e1 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:08:25 -0400 Subject: [PATCH 080/127] FEAT: 1-sided 3rd order gradient --- src/grid.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/grid.cpp b/src/grid.cpp index 14fce6ba..b20953b2 100644 --- a/src/grid.cpp +++ b/src/grid.cpp @@ -113,6 +113,12 @@ Grid::Grid(int nX_in, int nY_in, int nZ_in, int nGCs_in) { dalt_ratio_scgc.set_size(nX, nY, nZ); dalt_ratio_sq_scgc.set_size(nX, nY, nZ); + MeshCoef1s3rdp1.set_size(nX, nY, nGCs); + MeshCoef1s3rdp2.set_size(nX, nY, nGCs); + MeshCoef1s3rdp3.set_size(nX, nY, nGCs); + MeshCoef1s3rdp4.set_size(nX, nY, nGCs); + MeshCoef1s3rdp5.set_size(nX, nY, nGCs); + dlat_center_scgc.set_size(nX, nY, nZ); dlat_center_dist_scgc.set_size(nX, nY, nZ); From 5f23211ff8cc5fa69193373ae94e2a283e651caa Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:08:56 -0400 Subject: [PATCH 081/127] FEAT: BCs for the ions --- include/ions.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ions.h b/include/ions.h index bd8dd1ff..38138764 100644 --- a/include/ions.h +++ b/include/ions.h @@ -107,9 +107,9 @@ class Ions { void fill_electrons(); void calc_sound_speed(); void calc_cMax(); - void set_bcs(Grid grid); - void set_upper_bcs(Grid grid); - void set_lower_bcs(Grid grid); + bool set_bcs(Grid grid, Times time, Indices indices); + bool set_upper_bcs(Grid grid); + bool set_lower_bcs(Grid grid, Times time, Indices indices); int get_species_id(std::string name); void calc_efield(Grid grid); From 2653e15ec73c56af621b1059c03ab1b0a90c3f99 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:09:45 -0400 Subject: [PATCH 082/127] FEAT: improve ion BCs --- src/ions_bcs.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ions_bcs.cpp b/src/ions_bcs.cpp index bc423412..aae7cc73 100644 --- a/src/ions_bcs.cpp +++ b/src/ions_bcs.cpp @@ -77,10 +77,11 @@ bool Ions::set_upper_bcs(Grid grid) { species[iSpecies].temperature_scgc.slice(iAlt - 1); aveT = (species[iSpecies].temperature_scgc.slice(iAlt) + - electon_temperature_scgc.slice(iAlt)); + electron_temperature_scgc.slice(iAlt)); // Calculate scale height for the species: - h = cKB * species[iSpecies].temperature_scgc.slice(iAlt) / - (species[iSpecies].mass % abs(grid.gravity_vcgc[2])); + h = cKB / species[iSpecies].mass * + species[iSpecies].temperature_scgc.slice(iAlt) / + abs(grid.gravity_vcgc[2].slice(iAlt)); // Assume each species falls of with (modified) hydrostatic: species[iSpecies].density_scgc.slice(iAlt) = species[iSpecies].density_scgc.slice(iAlt - 1) % @@ -96,7 +97,7 @@ bool Ions::set_upper_bcs(Grid grid) { // set lower boundary conditions for the ions //---------------------------------------------------------------------- -bool Ions::set_lower_bcs(Grid grid) { +bool Ions::set_lower_bcs(Grid grid, Times time, Indices indices) { std::string function = "Ions::set_lower_bcs"; static int iFunction = -1; From 0c3bc18987dad0ed6c36272911149421286aabc1 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:10:25 -0400 Subject: [PATCH 083/127] FEAT: separate friction and heat exchange --- include/neutrals.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/neutrals.h b/include/neutrals.h index 664e9714..e99f7b3d 100644 --- a/include/neutrals.h +++ b/include/neutrals.h @@ -207,7 +207,10 @@ class Neutrals { arma_cube heating_chemical_scgc; // Bulk neutral collisional heating with ions (K/s) - arma_cube heating_ion_collisions_scgc; + arma_cube heating_ion_friction_scgc; + + // Bulk neutral collisional heating with ions (K/s) + arma_cube heating_ion_heat_transfer_scgc; // Total heating sources arma_cube heating_sources_total; From a4ebdbe8cfc5809a221b1b9c6dc69016169bb946 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:10:48 -0400 Subject: [PATCH 084/127] FEAT: separate friction and heat exchange --- src/neutrals.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/neutrals.cpp b/src/neutrals.cpp index 4e838644..fa722a97 100644 --- a/src/neutrals.cpp +++ b/src/neutrals.cpp @@ -140,8 +140,10 @@ Neutrals::Neutrals(Grid grid, heating_chemical_scgc.zeros(); heating_sources_total.set_size(nLons, nLats, nAlts); heating_sources_total.zeros(); - heating_ion_collisions_scgc.set_size(nLons, nLats, nAlts); - heating_ion_collisions_scgc.zeros(); + heating_ion_friction_scgc.set_size(nLons, nLats, nAlts); + heating_ion_friction_scgc.zeros(); + heating_ion_heat_transfer_scgc.set_size(nLons, nLats, nAlts); + heating_ion_heat_transfer_scgc.zeros(); O_cool_scgc.set_size(nLons, nLats, nAlts); O_cool_scgc.zeros(); NO_cool_scgc.set_size(nLons, nLats, nAlts); From b8e9d80cf931f6b2dadd54e227634f5461d027f2 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:11:46 -0400 Subject: [PATCH 085/127] FEAT: 1-sided gradient calculation --- include/solvers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/solvers.h b/include/solvers.h index 48515deb..35b8a64b 100644 --- a/include/solvers.h +++ b/include/solvers.h @@ -88,6 +88,7 @@ arma_cube calc_gradient_alt(arma_cube value, Grid grid); std::vector calc_gradient_vector(arma_cube value_scgc, Grid grid); std::vector calc_gradient_cubesphere(arma_cube value, Grid grid); arma_cube calc_gradient_alt_4th(arma_cube value, Grid grid); +arma_mat project_onesided_alt_3rd(arma_cube value, Grid grid, int64_t iAlt); // interpolation in 1D precision_t linear_interpolation(const precision_t y0, From 6694528b90ab80e64b383820e780ffc537d92967 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:12:18 -0400 Subject: [PATCH 086/127] FEAT: 1-sided gradient calculation --- src/solver_gradients.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/solver_gradients.cpp b/src/solver_gradients.cpp index f2bf0cc9..4eade015 100644 --- a/src/solver_gradients.cpp +++ b/src/solver_gradients.cpp @@ -159,6 +159,33 @@ arma_cube calc_gradient_alt_4th(arma_cube value, Grid grid) { return gradient; } + +// -------------------------------------------------------------------------- +// Calculate the 3rd order (on-sided) gradient in the altitudinal direction +// - this is only defined for the bottom ghostcells! +// -------------------------------------------------------------------------- + +arma_mat project_onesided_alt_3rd(arma_cube value, Grid grid, int64_t iAlt) { + + int64_t nLons = grid.get_nLons(); + int64_t nLats = grid.get_nLats(); + + arma_mat gradient(nLons, nLats), valueOut(nLons, nLats); + /* + gradient = + grid.MeshCoef1s3rdp1.slice(iAlt) % value.slice(iAlt + 1) + + grid.MeshCoef1s3rdp2.slice(iAlt) % value.slice(iAlt + 2) + + grid.MeshCoef1s3rdp3.slice(iAlt) % value.slice(iAlt + 3) + + grid.MeshCoef1s3rdp4.slice(iAlt) % value.slice(iAlt + 4) + + grid.MeshCoef1s3rdp5.slice(iAlt) % value.slice(iAlt + 5); + */ + gradient = (value.slice(iAlt + 2) - value.slice(iAlt + 1)) / + grid.dalt_lower_scgc.slice(iAlt + 2); + + valueOut = value.slice(iAlt + 1) - gradient % grid.dalt_lower_scgc.slice(iAlt + 1); + return valueOut; +} + // -------------------------------------------------------------------------- // Calculate the gradient in cubesphere spatial discretization // -------------------------------------------------------------------------- From 66f315da23805f86a12c518670de9e9c73abfc2a Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:12:40 -0400 Subject: [PATCH 087/127] FEAT: separate friction and heat exchange --- src/add_sources.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/add_sources.cpp b/src/add_sources.cpp index dbae9f33..a527a2fd 100644 --- a/src/add_sources.cpp +++ b/src/add_sources.cpp @@ -20,7 +20,8 @@ void Neutrals::add_sources(Times time, Planets planet, Grid grid) { heating_sources_total = heating_euv_scgc + heating_chemical_scgc - + heating_ion_collisions_scgc + + heating_ion_friction_scgc + //+ heating_ion_heat_transfer_scgc - O_cool_scgc - NO_cool_scgc; From 5a22402825b650f05310690f3c92140e1ca96b26 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:13:24 -0400 Subject: [PATCH 088/127] BUG: getting to work --- src/advance.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index 6e8b0e21..2770df42 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -47,7 +47,7 @@ bool advance(Planets &planet, neutrals.calc_bulk_velocity(); neutrals.calc_kappa_eddy(); neutrals.calc_viscosity(); - neutrals.calc_cMax(); + //neutrals.calc_cMax(); ions.fill_electrons(); ions.calc_sound_speed(); @@ -70,9 +70,12 @@ bool advance(Planets &planet, if (didWork) didWork = ions.set_bcs(gGrid, time, indices); - + + if (input.get_nAltsGeo() > 1) + neutrals.advect_vertical(gGrid, time); + neutrals.exchange_old(gGrid); - advect(gGrid, time, neutrals); + //advect(gGrid, time, neutrals); if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites("After Horizontal Advection"); @@ -121,9 +124,6 @@ bool advance(Planets &planet, ions.calc_ion_temperature(neutrals, gGrid, time); ions.calc_electron_temperature(neutrals, gGrid); - if (input.get_nAltsGeo() > 1) - neutrals.advect_vertical(gGrid, time); - if (didWork & input.get_check_for_nans()) didWork = neutrals.check_for_nonfinites("After Vertical Advection"); From cd7ca482dbbb441521a536cf9f66e0072a44ebef Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Thu, 30 May 2024 17:18:03 -0400 Subject: [PATCH 089/127] BUG: ionization doesn't have iwave index --- src/calc_euv.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calc_euv.cpp b/src/calc_euv.cpp index 09be872e..be2d4d8e 100644 --- a/src/calc_euv.cpp +++ b/src/calc_euv.cpp @@ -110,7 +110,6 @@ void calc_ionization_heating(Euv euv, neutrals.species[iSpecies].chapman_scgc.slice(iAlt); } } - intensity2d = euv.wavelengths_intensity_top[iWave] * exp(-1.0 * tau2d); for (iSpecies = 0; iSpecies < neutrals.nSpecies; iSpecies++) { @@ -146,7 +145,8 @@ void calc_ionization_heating(Euv euv, if (neutrals.species[iSpecies].iEuvIonSpecies_[iPei] == neutrals.species[iSpecies].iEuvIonSpecies_[iIonization]) { j_ = neutrals.species[iSpecies].iEuvIonId_[iPei]; - ionization2d[iWave] *= (1 + euv.waveinfo[j_].values[iWave]); + ionization2d *= (1 + euv.waveinfo[j_].values[iWave]); + std::cout << "here in the ipei loop\n"; } } @@ -156,6 +156,7 @@ void calc_ionization_heating(Euv euv, iIon = neutrals.species[iSpecies].iEuvIonSpecies_[iIonization]; ions.species[iIon].ionization_scgc.slice(iAlt) = ions.species[iIon].ionization_scgc.slice(iAlt) + ionization2d; + } // iIonization } // iSpecies } // iWave From fe1b68c90113c9ebb632d66c33c5e2f0f7e11be0 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:39:49 -0400 Subject: [PATCH 090/127] TESTING: GITM ignores bulk scale height --- src/calc_neutral_derived.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/calc_neutral_derived.cpp b/src/calc_neutral_derived.cpp index 8d24c414..404eb087 100644 --- a/src/calc_neutral_derived.cpp +++ b/src/calc_neutral_derived.cpp @@ -232,10 +232,13 @@ void Neutrals::calc_scale_height(Grid grid) { (species[iSpecies].mass * abs(grid.gravity_vcgc[2])); } + iSpecies = 3; + std::cout << "scale_height : " << species[iSpecies].scale_height_scgc(10,10,2) << "\n"; + // If we have eddy diffusion, the scale-heights need to be adjusted, // since all of the scale heights should be the same in the region // where eddy diffusion is dominant. - +/* if (input.get_use_eddy_momentum()) { // We need the mean major mass in the bottom-most cell, which we // assume is the region where the atmosphere is well-mixed: @@ -275,7 +278,7 @@ void Neutrals::calc_scale_height(Grid grid) { percentage % bulkH; } } - +*/ return; } @@ -374,7 +377,7 @@ void Neutrals::calc_cMax() { report.exit(function); return; } - +/* // ---------------------------------------------------------------------- // Calculate dt primarily for the spherical grid // ---------------------------------------------------------------------- @@ -489,6 +492,7 @@ precision_t Neutrals::calc_dt_cubesphere(Grid grid) { report.exit(function); return dt; } +*/ //---------------------------------------------------------------------- // Calculate the altitude integral of the different species for EUV From 2ae5d5a5a38a8cf9d9f0effb0149618f87ce9102 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:40:33 -0400 Subject: [PATCH 091/127] FEAT: 1-sided 3rd order gradient --- src/fill_grid.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/fill_grid.cpp b/src/fill_grid.cpp index a0f43fc7..00b39539 100644 --- a/src/fill_grid.cpp +++ b/src/fill_grid.cpp @@ -357,6 +357,34 @@ void Grid::calc_alt_grid_spacing() { // Need the square of the ratio: dalt_ratio_sq_scgc = dalt_ratio_scgc % dalt_ratio_scgc; + + // Calculate the one-sided 3rd order gradient coefficients for the lower BC: + + arma_mat h1, h2, h3, h4, MeshH1, MeshH2, MeshH3, MeshH4; + + for (iAlt = 0; iAlt < nGCs; iAlt++) { + h1 = dalt_lower_scgc.slice(iAlt + 2); + h2 = dalt_lower_scgc.slice(iAlt + 3); + h3 = dalt_lower_scgc.slice(iAlt + 4); + h4 = dalt_lower_scgc.slice(iAlt + 5); + MeshH1 = h1; + MeshH2 = h1 + h2; + MeshH3 = h1 + h2 + h3; + MeshH4 = h1 + h2 + h3 + h4; + MeshCoef1s3rdp1.slice(iAlt) = + -1.0*( MeshH2*MeshH3*MeshH4 + MeshH1*MeshH3*MeshH4 + + MeshH1*MeshH2*MeshH4 + MeshH1*MeshH2*MeshH3)/ + (MeshH1*MeshH2*MeshH3*MeshH4); + MeshCoef1s3rdp2.slice(iAlt) = + 1.0*( MeshH2*MeshH3*MeshH4)/(h1*h2*(h2 + h3)*(h2 + h3 + h4)); + MeshCoef1s3rdp3.slice(iAlt) = + -1.0*( MeshH1*MeshH3*MeshH4)/(MeshH2*h2*h3*(h3+h4)); + MeshCoef1s3rdp4.slice(iAlt) = + 1.0*( MeshH1*MeshH2*MeshH4)/(MeshH3*(h3+h2)*h3*h4); + MeshCoef1s3rdp5.slice(iAlt) = + -1.0*( MeshH1*MeshH2*MeshH3)/(MeshH4*(h2+h3+h4)*(h3+h4)*h4); + } + } // --------------------------------------- From 1e6e22c8da29ee7bde93e3fe54ec815733e1657c Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:41:23 -0400 Subject: [PATCH 092/127] FEAT: separate friction and heat transfer --- src/neutral_ion_collisions.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/neutral_ion_collisions.cpp b/src/neutral_ion_collisions.cpp index fb98dc98..0b8e4e07 100644 --- a/src/neutral_ion_collisions.cpp +++ b/src/neutral_ion_collisions.cpp @@ -35,6 +35,9 @@ void calc_ion_collisions(Neutrals &neutrals, // If we are using the bulk (horizontal, primarily) neutral winds // then approximate some of the collisional quantities + neutrals.heating_ion_friction_scgc.zeros(); + neutrals.heating_ion_heat_transfer_scgc.zeros(); + if (input.get_advection_neutrals_bulkwinds()) { for (iIon = 0; iIon < ions.nSpeciesAdvect; iIon++) { iIon_ = ions.species_to_advect[iIon]; @@ -49,7 +52,7 @@ void calc_ion_collisions(Neutrals &neutrals, // Now use the bulk quantities for the collisions // (beta is included in the last step) // heat transfer between ions and neutrals: - energy = 3 * cKB / ions.mean_major_mass_scgc % + neutrals.heating_ion_heat_transfer_scgc = 3 * cKB / ions.mean_major_mass_scgc % (ions.temperature_scgc - neutrals.temperature_scgc); for (iDir = 0; iDir < 3; iDir++) { // need the velocity difference for momentum and energy eqns: @@ -58,12 +61,14 @@ void calc_ion_collisions(Neutrals &neutrals, neutrals.acc_ion_collisions[iDir] = beta / neutrals.rho_scgc % vDiff; // Frictional heating between ions and neutrals: - energy = energy + vDiff % vDiff; + neutrals.heating_ion_friction_scgc = neutrals.heating_ion_friction_scgc + vDiff % vDiff; } // multiply by collision frequencies and convert // energy change to temperature change: - neutrals.heating_ion_collisions_scgc = - beta % energy / (2 * neutrals.rho_scgc % neutrals.Cv_scgc); + neutrals.heating_ion_friction_scgc = + beta % neutrals.heating_ion_friction_scgc / (2 * neutrals.rho_scgc % neutrals.Cv_scgc); + neutrals.heating_ion_heat_transfer_scgc = + beta % neutrals.heating_ion_friction_scgc / (2 * neutrals.rho_scgc % neutrals.Cv_scgc); } else { energy.zeros(); @@ -87,27 +92,36 @@ void calc_ion_collisions(Neutrals &neutrals, // Momentum = sum(B * (Vi - Vn)) // Energy = sum_neutrals(sum__ions(B/(Mi + Mn) * (Ti - Tn) + Mi * (Vi-Vn)^2)) - energy = energy + 3 * cKB * one_over_masses * + neutrals.heating_ion_heat_transfer_scgc = + neutrals.heating_ion_heat_transfer_scgc + + 3 * cKB * one_over_masses * (ions.temperature_scgc - neutrals.temperature_scgc); for (iDir = 0; iDir < 3; iDir++) { vDiff = (advected_ion.par_velocity_vcgc[iDir] + advected_ion.perp_velocity_vcgc[iDir] - advected_neutral.velocity_vcgc[iDir]); - energy = energy + (advected_ion.mass * one_over_masses) * vDiff % vDiff; + neutrals.heating_ion_friction_scgc = + neutrals.heating_ion_friction_scgc + + (advected_ion.mass * one_over_masses) * vDiff % vDiff; momentum[iDir] = momentum[iDir] + beta % vDiff; } // for each ion // - energy = energy % beta; + neutrals.heating_ion_friction_scgc = + neutrals.heating_ion_friction_scgc % beta; + neutrals.heating_ion_heat_transfer_scgc = + neutrals.heating_ion_heat_transfer_scgc % beta; } // for each ion // Divide by the mass density to get the acceleration for (iDir = 0; iDir < 3; iDir++) advected_neutral.acc_ion_drag[iDir] = momentum[iDir]/rho_n; } // for each neutral - // Only one heating needed for the neutrals: - neutrals.heating_ion_collisions_scgc = - energy / (neutrals.rho_scgc % neutrals.Cv_scgc); + // Convert from energy into K/s: + neutrals.heating_ion_friction_scgc = + neutrals.heating_ion_friction_scgc / (neutrals.rho_scgc % neutrals.Cv_scgc); + neutrals.heating_ion_heat_transfer_scgc = + neutrals.heating_ion_heat_transfer_scgc / (neutrals.rho_scgc % neutrals.Cv_scgc); } // bulk neutral winds report.exit(function); From 85587d8c5c97a5afb08aae5553c2dd5f02af7e71 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:41:57 -0400 Subject: [PATCH 093/127] FEAT: improve lower BCs --- src/neutrals_bcs.cpp | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/neutrals_bcs.cpp b/src/neutrals_bcs.cpp index eb1aa5e5..6cf4a066 100644 --- a/src/neutrals_bcs.cpp +++ b/src/neutrals_bcs.cpp @@ -189,26 +189,43 @@ bool Neutrals::set_lower_bcs(Grid grid, report.print(2, "setting lower bcs to planet"); - // Set the lower boundary condition: + // Set the lower boundary condition in the last ghost cell: for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { - species[iSpecies].density_scgc.slice(0). + species[iSpecies].density_scgc.slice(nGCs - 1). fill(species[iSpecies].lower_bc_density); } - temperature_scgc.slice(0).fill(initial_temperatures[0]); + temperature_scgc.slice(nGCs - 1).fill(initial_temperatures[0]); didWork = true; } // fill the second+ grid cells with the bottom temperature: - for (iAlt = 1; iAlt < nGCs; iAlt++) - temperature_scgc.slice(iAlt) = temperature_scgc.slice(iAlt - 1); - - // fill the second+ grid cells with a hydrostatic solution: - for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) - fill_with_hydrostatic(iSpecies, 1, nGCs, grid); - + for (iAlt = nGCs - 2; iAlt >= 0; iAlt--) + temperature_scgc.slice(iAlt) = temperature_scgc.slice(iAlt + 1); + + arma_mat sh_ave; + // fill the lower ghost cells with a hydrostatic solution: + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { + for (iAlt = nGCs - 2; iAlt >= 0; iAlt--) { + sh_ave = + (species[iSpecies].scale_height_scgc.slice(iAlt) + + species[iSpecies].scale_height_scgc.slice(iAlt + 1))/2; + + species[iSpecies].density_scgc.slice(iAlt) = + temperature_scgc.slice(iAlt + 1) / + temperature_scgc.slice(iAlt) % + species[iSpecies].density_scgc.slice(iAlt + 1) % + exp(grid.dalt_lower_scgc.slice(iAlt) / sh_ave); + } + for (iAlt = nGCs - 1; iAlt >= 0; iAlt--) { + //std::cout << "before project : " << iAlt << " " << iSpecies << " " + // << species[iSpecies].velocity_vcgc[2](10,10,2) << "\n"; + species[iSpecies].velocity_vcgc[2].slice(iAlt) = + project_onesided_alt_3rd(species[iSpecies].velocity_vcgc[2], grid, iAlt); + } + } // Force vertical velocities to be zero in the ghost cells: - for (iDir = 0; iDir < 3; iDir++) { + for (iDir = 0; iDir < 2; iDir++) { for (iAlt = 0; iAlt < nGCs; iAlt++) { for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { // species velocity: @@ -216,10 +233,12 @@ bool Neutrals::set_lower_bcs(Grid grid, } // bulk velocity: - velocity_vcgc[iDir].slice(iAlt).zeros(); + //velocity_vcgc[iDir].slice(iAlt).zeros(); } } + calc_bulk_velocity(); + if (!didWork) { report.error("issue with lower BCs!"); report.error("maybe check boundaryconditions type : " + bcsType); From 9141b6436ece10bc28855f893a4fb4ba4dbf6df0 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:43:04 -0400 Subject: [PATCH 094/127] TESTING: include sources in conduction or not --- src/neutrals_energy.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/neutrals_energy.cpp b/src/neutrals_energy.cpp index da6f0bf6..ce63849c 100644 --- a/src/neutrals_energy.cpp +++ b/src/neutrals_energy.cpp @@ -57,9 +57,8 @@ void Neutrals::update_temperature(Grid grid, Times time) { rhocvr21d = rhocvr23d.tube(iLon, iLat); sources1d = heating_sources_total.tube(iLon, iLat); - temp1d = temp1d + dt * sources1d; - sources1d.zeros(); - + //temp1d = temp1d + dt * sources1d; + //sources1d.zeros(); dalt1d = grid.dalt_lower_scgc.tube(iLon, iLat); conduction1d.zeros(); From 9101ed1c12b4533ce9bf19647a9ffaed36da5e9c Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:44:21 -0400 Subject: [PATCH 095/127] FEAT: improve lower BCs --- src/neutrals_ics.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/neutrals_ics.cpp b/src/neutrals_ics.cpp index 02fb258f..6a5be4de 100644 --- a/src/neutrals_ics.cpp +++ b/src/neutrals_ics.cpp @@ -31,6 +31,7 @@ bool Neutrals::initial_conditions(Grid grid, int64_t iLon, iLat, iAlt, iA; precision_t alt, r; int64_t nAlts = grid.get_nZ(); + int64_t nGCs = grid.get_nGCs(); report.print(3, "Creating Neutrals initial_condition"); @@ -160,15 +161,13 @@ bool Neutrals::initial_conditions(Grid grid, temperature_scgc.tube(iLon, iLat) = temp1d; } - // Set the lower boundary condition: - for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) { - species[iSpecies].density_scgc.slice(0). - fill(species[iSpecies].lower_bc_density); - } - + // Make the initial condition in the lower ghost cells to be consistent + // with the actual lowwer BC: calc_scale_height(grid); + set_lower_bcs(grid, time, indices); + for (int iSpecies = 0; iSpecies < nSpecies; iSpecies++) - fill_with_hydrostatic(iSpecies, 1, nAlts, grid); + fill_with_hydrostatic(iSpecies, nGCs, nAlts, grid); } // type = planet } From e89e40188e0625c7a376c1d0d2c759b5ca148cd9 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:45:29 -0400 Subject: [PATCH 096/127] TESTING: return acceleration or new velocity --- src/neutrals_momentum_friction.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/neutrals_momentum_friction.cpp b/src/neutrals_momentum_friction.cpp index e92a3f34..f585cdf7 100644 --- a/src/neutrals_momentum_friction.cpp +++ b/src/neutrals_momentum_friction.cpp @@ -66,11 +66,9 @@ arma_vec Neutrals::calc_friction_one_cell(int64_t iLon, int64_t iLat, int64_t iA // Solve system of equations: arma_vec new_vels = arma::solve(matrix, vels, solve_opts::fast); - /* // put the new values into the velocity cubes: for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) accs(iSpecies) = new_vels(iSpecies) - vels(iSpecies); - */ report.exit(function); return new_vels; } @@ -118,14 +116,14 @@ void Neutrals::calc_neutral_friction() { species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt); } - //acc = neutral_friction_one_cell(iLon, iLat, iAlt, vels); - new_vels = calc_friction_one_cell(iLon, iLat, iAlt, vels); + // = neutral_friction_one_cell(iLon, iLat, iAlt, vels); + acc = calc_friction_one_cell(iLon, iLat, iAlt, vels); for (iSpecies = 0; iSpecies < nSpeciesAdvect; iSpecies++) { iSpecies_ = species_to_advect[iSpecies]; - species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt) = new_vels(iSpecies); - //species[iSpecies_].acc_neutral_friction[iDir](iLon, iLat, iAlt) = - // acc(iSpecies); + //species[iSpecies_].velocity_vcgc[iDir](iLon, iLat, iAlt) = new_vels(iSpecies); + species[iSpecies_].acc_neutral_friction[iDir](iLon, iLat, iAlt) = + acc(iSpecies); } // iSpeciesAdvect //} // for direction } // for long From 4a9735a6c4b9faa7e1907842ab500487a51d2edd Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:46:06 -0400 Subject: [PATCH 097/127] FEAT: separate friction and heat transfer --- src/output.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index d30416b4..cd01604f 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -247,10 +247,14 @@ bool output(const Neutrals &neutrals, "Heating from Chemistry", "K/s", neutrals.heating_chemical_scgc); - AllOutputContainers[iOutput].store_variable("Heating_Collisions", - "Heating from Ion Neutral Collisions", + AllOutputContainers[iOutput].store_variable("Heating_Transfer", + "Heating from Ti- Tn Ion Neutral Collisions", "K/s", - neutrals.heating_ion_collisions_scgc); + neutrals.heating_ion_heat_transfer_scgc); + AllOutputContainers[iOutput].store_variable("Heating_Ion_Friction", + "Heating from Friction Ion Neutral Collisions", + "K/s", + neutrals.heating_ion_friction_scgc); AllOutputContainers[iOutput].store_variable("Conduction", "Conduction", "K/s", @@ -297,9 +301,6 @@ bool output(const Neutrals &neutrals, if (type_output == "bfield") filename = "3DBFI_"; - if (type_output == "moment") - filename = "3DMMT_"; - if (type_output == "gravity") filename = "3DGRA_"; From ce5015b6b80c7ad3367e09f6f00b8513ce57e27b Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:46:50 -0400 Subject: [PATCH 098/127] TESTING: trying to get this to work! --- src/solver_vertical_rusanov.cpp | 53 ++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/solver_vertical_rusanov.cpp b/src/solver_vertical_rusanov.cpp index b59baf88..cc52871d 100644 --- a/src/solver_vertical_rusanov.cpp +++ b/src/solver_vertical_rusanov.cpp @@ -76,20 +76,6 @@ void calc_facevalues_alts_rusanov(Grid &grid, for (iY = nGCs; iY < nYs - nGCs; iY++) dVarLimited(iX, iY, iZ) = limiter_mc(dVarUp(iX, iY), dVarDown(iX, iY), beta); - - // TEMPORARY!!! - iZ = 18; - ida = 2.0 / grid.dalt_lower_scgc.slice(iZ + 1); - dVarUp = ida % - (factor1 * (inVar.slice(iZ + 1) - inVar.slice(iZ)) - - factor2 * (inVar.slice(iZ + 2) - inVar.slice(iZ - 1))); - - ida = 2.0 / grid.dalt_lower_scgc.slice(iZ); - dVarDown = ida % - (factor1 * (inVar.slice(iZ) - inVar.slice(iZ - 1)) - - factor2 * (inVar.slice(iZ + 1) - inVar.slice(iZ - 2))); - - // End TEMP for (iZ = nGCs; iZ < nZs - nGCs + 1; iZ++) { outLeft.slice(iZ) = @@ -309,7 +295,6 @@ void Neutrals::solver_vertical_rusanov(Grid grid, // calculate vertical momentum due to eddy diffusion: vertical_momentum_eddy(grid); - // ----------------------------------------------------------- // Now calculate new states: precision_t mass; @@ -339,7 +324,7 @@ void Neutrals::solver_vertical_rusanov(Grid grid, - species[iSpecies].acc_eddy - acc_coriolis[2] - grid.cent_acc_vcgc[2] - + 0.25 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + + 1.0 * (temperature_scgc % gradLogN_s[iSpecies] * cKB / mass + gradTemp * cKB / mass + abs(grid.gravity_vcgc[2]))) + dt * diffVertVel_s[iSpecies]; @@ -389,8 +374,8 @@ void Neutrals::solver_vertical_rusanov(Grid grid, for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) if (species[iSpecies].DoAdvect) - species[iSpecies].newVelocity_vcgc[2].clamp(-150,150); - newTemperature_scgc.clamp(10, 1e32); + species[iSpecies].newVelocity_vcgc[2].clamp(-200,200); + newTemperature_scgc.clamp(150, 1e32); for (iX = nGCs; iX < nXs - nGCs; iX++) for (iY = nGCs; iY < nYs - nGCs; iY++) @@ -411,17 +396,43 @@ void Neutrals::solver_vertical_rusanov(Grid grid, } } +iX = 2; +iY = 4; // Force the neutrals to move together with friction: + iSpecies = 3; + std::cout << "ver sol, before friction: " + << species[iSpecies].velocity_vcgc[2](iX,iY,1) << " " + << species[iSpecies].velocity_vcgc[2](iX,iY,2) << " " + << species[iSpecies].velocity_vcgc[2](iX,iY,3) << " " + << species[iSpecies].velocity_vcgc[2](iX,iY,4) << "\n"; + + mass = species[iSpecies].mass; + + for (int iAlt = 0; iAlt < 20; iAlt++) { + std::cout << iAlt << " " + << log(species[iSpecies].density_scgc(iX, iY,iAlt)) << " " + << temperature_scgc(iX,iY,iAlt) << " " + << species[iSpecies].velocity_vcgc[2](iX,iY,iAlt) << " " + << temperature_scgc(10,10,iAlt) * gradLogN_s[iSpecies](iX,iY,iAlt) * cKB / mass << " " + << gradTemp(iX,iY,iAlt) * cKB / mass << " " + << abs(grid.gravity_vcgc[2](iX,iY,iAlt)) << "\n"; + } + calc_neutral_friction(); - /* + std::cout << "ver acc, after friction: " + << species[iSpecies].acc_neutral_friction[2](iX,iY,1) << " " + << species[iSpecies].acc_neutral_friction[2](iX,iY,2) << " " + << species[iSpecies].acc_neutral_friction[2](iX,iY,3) << " " + << species[iSpecies].acc_neutral_friction[2](iX,iY,4) << "\n"; + for (iSpecies = 0; iSpecies < nSpecies; iSpecies++) { if (species[iSpecies].DoAdvect) { species[iSpecies].velocity_vcgc[2] = species[iSpecies].velocity_vcgc[2] + dt * - species[iSpecies].acc_neutral_friction[iDir]; + species[iSpecies].acc_neutral_friction[2]; } } - */ + std::cout << "here\n"; calc_mass_density(); // Calculate bulk vertical winds: velocity_vcgc[2].zeros(); From c615c0f61503d863d34220afe1297320bd983424 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:47:39 -0400 Subject: [PATCH 099/127] BUG: sprintf to snprintf + reduce CFL for tesing --- src/time.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/time.cpp b/src/time.cpp index 385c29ff..80cd187d 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -107,7 +107,7 @@ int Times::check_time_gate(precision_t dt_check) { void Times::calc_dt(precision_t dtNeutral, precision_t dtIon) { dt = end - current; - double cfl = 0.5; + double cfl = 0.1; if (cfl * dtNeutral < dt) dt = cfl * dtNeutral; @@ -230,15 +230,15 @@ void Times::increment_time() { milli = iCurrent[6]; char tmp[100]; - sprintf(tmp, "%04d%02d%02d_%02d%02d%02d", + snprintf(tmp, 100, "%04d%02d%02d_%02d%02d%02d", year, month, day, hour, minute, second); sYMD_HMS = std::string(tmp); - sprintf(tmp, "%04d%02d%02d_%02d%02d%02d", + snprintf(tmp, 100, "%04d%02d%02d_%02d%02d%02d", year, month, day, hour, minute, 0); sYMD_HM0 = std::string(tmp); - sprintf(tmp, "%04d%02d%02d", year, month, day); + snprintf(tmp, 100, "%04d%02d%02d", year, month, day); sYMD = std::string(tmp); - sprintf(tmp, "%02d%02d%02d", hour, minute, second); + snprintf(tmp, 100, "%02d%02d%02d", hour, minute, second); sHMS = std::string(tmp); // Calculate Julian Day (day of year): From bd635d52e77e4afa95c4df9fdb011acee31870b5 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:48:30 -0400 Subject: [PATCH 100/127] BUG: incorporate electrodynamics file into electrodynamics --- share/run/aether.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/share/run/aether.json b/share/run/aether.json index 8a135d53..f0f49f82 100644 --- a/share/run/aether.json +++ b/share/run/aether.json @@ -26,10 +26,9 @@ "Electrodynamics" : { "Potential" : "Weimer05", - "DiffuseAurora" : "fta"}, + "DiffuseAurora" : "fta", + "File" : "UA/inputs/b20110320n_omni.bin"}, - "ElectrodynamicsFile" : "UA/inputs/b20110320n_omni.bin", - "Outputs" : { "type" : ["states"], "dt" : [900] }, From 98d1f9ad3691e54bfdba1b963067fe7cb5e727bf Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Fri, 31 May 2024 09:49:14 -0400 Subject: [PATCH 101/127] BUG: need r to interpret \ now --- srcPython/postAether.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srcPython/postAether.py b/srcPython/postAether.py index 0282138e..99f69a06 100755 --- a/srcPython/postAether.py +++ b/srcPython/postAether.py @@ -463,11 +463,11 @@ def get_core_file(filename): isEnsemble = False ensembleFile = '' ensembleNumber = -1 - m = re.match('.*([0123]D.*)(_g\d*)(\..*)',filename) + m = re.match(r'.*([0123]D.*)(_g\d*)(\..*)',filename) if m: coreFile = m.group(1) # check if file is a member of an ensemble: - check = re.match('.*([0123]D.*)(_m)(\d*)',coreFile) + check = re.match(r'.*([0123]D.*)(_m)(\d*)',coreFile) if (check): ensembleFile = check.group(1) isEnsemble = True From 42c8b978ec05f163364541208c607e6a4c4e7d50 Mon Sep 17 00:00:00 2001 From: "abukowski21@gmail.com" Date: Wed, 24 Jul 2024 10:05:23 -0400 Subject: [PATCH 102/127] DOC: Organize root README While working on "overhauling" (re-organizing) the documentation, I've noticed a lot of repeated & out-dated informatuion. This will be fixed in several commits. This commit will: - Add a TOC & remove info better placed elsewhere from `~/README.md` - Fix some typos in README - Standardize apple OS as MacOS, not Mac OS(X) - Shorten the root readme to only contain info necessary for a quick start - Apply conststent .md formatting --- README.md | 302 +++++++++++++++++++++++++----------------------------- 1 file changed, 142 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index 4af46445..b28a8007 100644 --- a/README.md +++ b/README.md @@ -1,170 +1,140 @@ # Aether -This is the home of the Aether model of the thermosphere and ionosphere. - -The Aether model has been developed using gnu c++ (versions 9, 10, 11). If -you are using this, hopefully it will just work out of the box. We have -been doing development of Aether on Mac OSX, and in Ubuntu Linux. We have -also used the Windows Subsystem for Linux, Ubuntu distribution, which -works similarly to the native Linux distribution. - -If you are a student and don't know how to work with a large code base -(i.e., multiple source codes in multiple directories), you may consider -starting with the doc/student.md file. - -## Dependencies: - -On Mac, installing some of the dependencies can be awkward, depending -on which c++ compiler you are using. Since there is one that -essentially comes with Mac OSX, called clang, the default compiler is -often this. Much of the other software is not built with this, so you -need to switch compilers, which can be challenging. - -While the development team has tried to remove as many of the -dependencies as possible to reduce this issues, it is still a good -idea to try to get a different compiler to work. If you want to -use netCDF files or the fortran compiled options (like MSIS), you -will probably have to do this. - -0. Install gcc. This comes install by default on Ubuntu. For MacOS, -this can be installed using [macports](https://www.macports.org/): -```bash -sudo port install gcc11 -``` -(At this moment, gcc11 is the latest version, there may be a more up-to-date version available now.) - - -1. Aether uses [CMake](https://cmake.org/) instead of make. If you don't have it installed, you need it. - -For MacOS, this can be installed using -[macports](https://www.macports.org/) -```bash -sudo port install cmake -``` - -Or using [homebrew](https://formulae.brew.sh/formula/cmake): -```bash -sudo brew install cmake -``` -The development team has mostly used macports and not homebrew. - -For Ubuntu/Debian Linux: -```bash -sudo apt install cmake -``` -This can be done on redhat using yum also. - -2. Aether uses the nlohman json package for reading and writing json files. - -On Ubuntu: - -```bash -sudo apt-get install -y nlohmann-json3-dev -``` - -On Mac: - -```bash -sudo port install nlohmann-json -``` - -3. The armadillo headers need to be installed. Simplistically, -Armadillo is a system that allows matrix math to be done in C++ -easily. We mostly use it for doing math with matrices (like -multiplication, addition, etc.), but it is much more powerful than -this. You will notice that there are not many 3D loops in Aether, -which is due to Armadillo. To make this all fast, it is best to -install the lapack abd blas libraries too. - -On Ubuntu: - -```bash -sudo apt-get install liblapack-dev -sudo apt-get install libblas-dev -sudo apt-get install libboost-dev -sudo apt-get install libarmadillo-dev -sudo apt-get install openmpi-bin libopenmpi-dev -``` - -On Mac: - -```bash -sudo port install lapack -sudo port install OpenBLAS -sudo port install boost -sudo port install armadillo -sudo port install openmpi-bin libopenmpi-dev - ``` - -4. We have removed the strict dependency for netcdf, but a lot of -codes used netCDF, so it doesn't hurt to try to install the libraries. -Aether uses the netcdf library (netcdf-cxx4). As above, netCDF can be -installed using a package manager. - -On Mac, if you want the clang compiled version of netcdf, then: -```bash -sudo port install netcdf-cxx4 -``` - -If you want the gcc version of netcdf, then: -```bash -sudo port install netcdf-cxx4 +gcc10 -``` -On Ubuntu, gcc is the default compiler, it seems like you can probably just do: -```bash -sudo apt-get install libnetcdf-dev -sudo apt install libnetcdf-c++4-dev -``` - -## Quick Start: - -These are unix commands, assuming that you have access to a unix/linux -terminal. This has been tested on a MacBook Pro and Ubuntu. (This is -assuming that you are installing the root version of Aether and not a -forked version. If you are using a forked version, replace the -"AetherModel" with the appropriate location of the fork.) +This is the home of the Aether model of the thermosphere and ionosphere. -```bash -git clone https://github.com/AetherModel/Aether +The Aether model has been developed using GNU C++ (versions 9, 10, 11). If you +are using this, hopefully it will just work out of the box. We have been doing +development of Aether on MacOS and Ubuntu Linux. We have also used the Windows +Subsystem for Linux, Ubuntu distribution, which works similarly to the native +Linux distribution. + +> If you are a student and don't know how to work with a large code base (i.e., +multiple source codes in multiple directories), you may consider starting within +the [Students](doc/student.md) page. + +All other users may wish to continue with the short installation guide below. If +you run into issues or have questions, first consult the in-progress +[documentation](doc/README.md). More complete documentation is actively being +developed. + +## Contents + +- [Aether](#aether) + - [Contents](#contents) + - [Quick Start](#quick-start) + - [Dependencies](#dependencies) + - [Getting the Code](#getting-the-code) + - [Compiling \& Running](#compiling--running) + - [Code Manual](#code-manual) + - [Further Documentation](#further-documentation) + +## Quick Start + +The following guide serves to demonstrate how to download, install, and then run +the Aether model. More details on each topic will be linked, and some more +detail is available [here](doc/README.md). + +### Dependencies + +On MacOS, installing some of the dependencies can be awkward, depending on which +C++ compiler you are using. Since there is one that essentially comes with +MacOS, called `clang`, the default compiler is often this. Much of the other +software is not built with this, so you need to switch compilers, which can be +challenging. + +While the development team has tried to remove as many of the dependencies as +possible to reduce this issues, it is still a good idea to try to get a +different compiler to work. If you want to use NetCDF files or the Fortran +compiled options (like MSIS), you will probably have to do this. + +A package manager is recommended to install the dependencies of the Aether +model. More details on the use of package managers can be found on [the +dependencies page](doc/installation/dependencies.md). For MacOS and Ubuntu Linux, the validated configurations are: + +
+ MacOS + + | Dependency | Tested version | + |---------------|----------------| + | armadillo | 11.4 | + | boost | 1.76 | + | cmake | 2.24 | + | gcc | 10, 11 | + | netcdf | 4.9 | + | netcdf-cxx4 | 4.3 | + | nlohmann-json | 3.11 | + | OpenBLAS | 0.3 | + | mpich | 4.1 | + + MacOS has two predominant package managers: [Homebrew](https://brew.sh) and + [MacPorts](https://www.macports.org/). Either will work. + +
+ +
+ Linux + + | Dependency | Tested version | + |--------------------|----------------| + | cmake | 2.24 | + | gcc | 10, 11, 12 | + | libarbadillo-dev | | + | libblas-dev | | + | libboost-dev | | + | liblapack-dev | | + | libnetcdf-dev | | + | libnetcdf-c++4-dev | | + | libopenmpi-dev | | + | nlohmann-json3-dev | | + | openmpi-bin | same as gcc | + + The specific package manager to use depends on which distribution of Linux + Aether is installed on. More details on Linux package managers can be found + [here, for + example](https://www.linode.com/docs/guides/linux-package-management-overview/#comparison-of-package-managers). + +
+ +### Getting the Code + +This has been tested on a MacBook Pro and Ubuntu. + +```bash +git clone git@github.com:AetherModel/Aether.git cd Aether git checkout develop ``` +> ***NOTES:*** +> +> - (This is assuming that you are installing the root version of Aether and not +a forked version. If you are using a forked version, replace the +"`AetherModel`" with the appropriate location of the fork.) +> - GitHub now recommends cloning repositories via `ssh` instead of `https`. For +development work, this is the way to go. If the above `git clone` command fails +and you are not planning on contributing to Aether development, the first line +can be replaced with "`git clone https://github.com/AetherModel/Aether`". For +help setting up your machine to use `ssh` to connect to Github, see [this +page](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) + +### Compiling & Running + To compile Aether: + ```bash mkdir build cd build cmake .. make [-j4] ``` -The -j4 is optional and uses 4 processors, but you could use 2, 4, 8, -or whatever. - -To compile Aether with NetCDF: -```bash -mkdir build -cd build -cmake -DUSE_NETCDF=Y .. -make [-j4] (the -j4 uses 4 processors) -``` -To compile Aether with FORTRAN codes: -```bash -mkdir build -cd build -cmake -DUSE_FORTRAN=Y .. -make [-j4] (the -j4 uses 4 processors) -``` +The `-j4` is optional and uses 4 processors, but you could use 2, 4, 8, or +whatever. -To compile Aether with double precision: -```bash -mkdir build -cd build -cmake -DUSE_DOUBLE_PRECISION=Y .. -make [-j4] (the -j4 uses 4 processors) -``` +More compilation options can be found on the [Compiling +Aether](doc/installation/build_opts.md) page. -Once you have compiled you can install Aether with an example run directory +Once you have compiled, you can install Aether with an example run directory structure like this: ```bash @@ -177,16 +147,17 @@ cd run.test There are essentially two input files that specify the settings in the code. When you are in a run directory, they are: -1. UA/inputs/defaults.json. These set the default inputs for the run -and should not be modified at all. You can look at these and copy the -settings that you want to change to this file: +1. UA/inputs/defaults.json. These set the default inputs for the run and should +not be modified at all. You can look at these and copy the settings that you +want to change to this file: -2. aether.json. This file can and should be modified to direct the -code to run the way that you would like. You can copy settings from -the default.json file and then modify them here. This will be covered -in the reference manual, once we have written one. +2. aether.json. This file can and should be modified to direct the code to run +the way that you would like. You can copy settings from the default.json file +and then modify them here. This will be covered in the reference manual, once we +have written one. -You can check to make sure that these are valid json files (not checking the content, though) with: +You can check to make sure that these are valid json files (not checking the +content, though) with: ```bash cd run.test @@ -194,7 +165,8 @@ python -m json.tool aether.json python -m json.tool UA/inputs/defaults.json ``` -At this time, there is no checker to see if all of the settings in each of the inputs files are actually valid and Aether understands them. +At this time, there is no checker to see if all of the settings in each of the +inputs files are actually valid and Aether understands them. Output files are in UA/output. @@ -202,7 +174,7 @@ We are working on aetherpy to make plots. Compare png files to tests/outputs_pngs/*.png to see if they are similar. -## Code Manual: +## Code Manual To create the code documentation manual, download Doxygen for your operating system and run: @@ -211,3 +183,13 @@ system and run: cd doc doxygen Doxyfile ``` + +## Further Documentation + +A more complete documentation is in +[development](https://github.com/AetherModel/AetherDocumentation), which can be +browsed [here](https://aetherdocumentation.rtfd.io/). + +The collection of Markdown files within the [doc](doc/README.md) folder of this +repository will provide more information and, at the time of writing, is more up +to date than the official documentation. It is recommended to start there. From 3d8e72b72a3e1d89ed1a840a3538486ff004baef Mon Sep 17 00:00:00 2001 From: "abukowski21@gmail.com" Date: Wed, 24 Jul 2024 10:11:33 -0400 Subject: [PATCH 103/127] DOC/STY: Markdown formatting Apply consistent markdown formatting (1 blank line, sequential header levels, etc.) and fix small typos. Also change iOS in bug_report to MacOS/Linux, since nobody will be running Aether on iOS --- .github/ISSUE_TEMPLATE/bug_report.md | 8 ++++--- doc/design/standards.md | 4 +--- doc/student.md | 32 ++++++++++++---------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 084139d6..cdaa9a9a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,6 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,9 +25,10 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **System information (please complete the following information):** - - OS: [e.g. iOS] - - Compilers used, with version numbers - - Otheer details about your setup that could be relevant + +- OS: (e.g. MacOS, Linux distribution, etc.) +- Compilers used, with version numbers +- Otheer details about your setup that could be relevant **Additional context** Add any other context about the problem here. diff --git a/doc/design/standards.md b/doc/design/standards.md index 0f2e52ca..af071cc4 100644 --- a/doc/design/standards.md +++ b/doc/design/standards.md @@ -4,7 +4,7 @@ Aether Coding and Design Standards Document Overall Design Philosophy ------------------------- -### Files +## Files Directory Structure: @@ -14,7 +14,5 @@ Directory Structure: - Names should be short but descriptive (less than 25 characters) - Source term files should start with `calc` - Variable naming --------------- - diff --git a/doc/student.md b/doc/student.md index 669c1bff..5ba7c855 100644 --- a/doc/student.md +++ b/doc/student.md @@ -1,6 +1,5 @@ # Student Walk Through of Aether - ## Start with the Basics 0. [Using the terminal - Unix commands](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) @@ -13,26 +12,21 @@ 4. [Tutorial for git and GitHub](https://www.freecodecamp.org/news/git-and-github-for-beginners/) - ## Some Editors The Classics: 1. [Emacs](http://www.jesshamrick.com/2012/09/10/absolute-beginners-guide-to-emacs/) - -emacs can be installed using a package manager such as apt or port - + - emacs can be installed using a package manager such as apt or port 2. [vim](https://linuxconfig.org/vim-tutorial) 21st Century Editors: 1. Most people are using Visual Studio Code to work on Aether. - 2. There are always popular Integrated Development Environments (IDEs) that can be used to edit, develop, and test code. If you use one and develop on Aether, please feel free to update this section. - ## Install Aether for Development Make sure you have all of the dependencies installed (see installation.md). @@ -40,17 +34,13 @@ Then start by creating a new fork of the Aether repository on GitHub. To do this: 1. Go to the Aether repository and click on "Fork" then "+ Create a new fork" - 2. Unclick the "Copy the main branch only" checkbox - 3. Click "Create fork" - 4. GitHub will take you to this repository automatically. It is now your "version" of Aether, and all development will be done here. We will use the username "YourRepo" instead of your actual repository in the examples below. - ```bash git clone https://github.com/YourRepo/Aether cd Aether @@ -58,8 +48,8 @@ git checkout develop ``` To compile Aether, you need to make sure you have all of the dependencies -installed (see the main README.md in the Aether directory), and then run -these commands: +installed (see the main README.md in the Aether directory or [this +page](installation/dependencies.md)), and then run these commands: ```bash mkdir build @@ -68,21 +58,24 @@ cmake .. make ``` -Once you have compiled you can run Aether. To remember which runs you're doing, +Once you have compiled, you can run Aether. To remember which runs you're doing, we recommend creating a specific directory for each run. This can be done by -copying the default run directory to have a special name, like this: +copying the default run directory to have a special name, like: + ```bash cd .. cp -R share/run ./run.test ``` Now, run the executable from the directory you created. + ```bash cd run.test ./aether ``` You should see something like: + ```bash run.test% ./aether > Need to NOT adjust F10.7, but that isn't included yet!!! @@ -102,6 +95,7 @@ etc. ``` The successful end of this will show a timing summary, similar to: + ```bash >main>advance>Chemistry::calc_chemistry nTimes called : 720 @@ -116,13 +110,15 @@ The successful end of this will show a timing summary, similar to: Now, edit the aether.json file and add the following after the "Debug" section, where "Your Name" should be your name: + ```bash "Student" : { - "name" : "Your Name", - "is" : true }, + "name" : "[Your Name]", + "is" : true }, ``` Then, run aether again. Your output should now look something like this: + ```bash run.test% ./aether > Hello Aaron - welcome to Aether! @@ -147,6 +143,7 @@ run.test% ./aether If something happened that made the input file unreadable, you'll instead see something like this: + ```bash Error in reading input file! input initialization failed! @@ -205,4 +202,3 @@ While you are awaiting a response from your advisor, you can check out the Issues part of the Aether github site. That will let you know all of the outstanding issues that are being worked on. Maybe you can contribute to some of these! - From e05f8210266c5547dedbd82888673d75dfd173de Mon Sep 17 00:00:00 2001 From: "abukowski21@gmail.com" Date: Wed, 24 Jul 2024 10:28:17 -0400 Subject: [PATCH 104/127] DOC: Organize .md files in `doc/` `doc/`'s markdown files were not organized, and had a lot of repeated information. This commit (along with #42c8b97 and #38de72) should help address that. There are still a number of gaps in doc coverage & some repeated information, but it should be easier to find specific information now. Features: - table of contents at ~/README.md and ~/doc/README.md - I put this into a tree that made sense to me, open to suggestions on how to re-organize. Thinking about breaking off parts of doc/usage/ to doc/internals, but it's not done yet. - Folders are Design (unchanged), Installing (package manager, cmake args, etc), and Usage (how to run, debug, and ensembles/indices). - Add note that the "real documentation" is in the works... see github.com/AetherModel/AetherDevelopment - I didn't want to touch this, but can migrate these README's there if it would be helpful. - Add consistent markdown formatting. Did not change *too* much of the existing content. But re-organized it and fixed typos/style where necessary. Spent a while debating whether to make this a GitHub Pages or add in to the RTD. Decided this would be easiest since it can be quickly adapted to either... But it's much less elegant that either would be. Navigation is particularly difficult, but I would imagine that people will start in the ~/README.md and then get linked to a specific page, then go back. Or will be hunting for a specific doc page which should be easier to find. If it's not easier, I can re-organize or add more navigation options. I was afraid of deleting too much of the read-the-docs (since a lot of info there seems irrelevant), so didn't do anything there. But I can. Note the code-of-conduct is still showing "problems" in a markdown linter, but I do not see it necessary to fix those. All other markdown files (outside of ~/.github/) show no errors. --- doc/README.md | 37 ++++++++ doc/installation.md | 114 ---------------------- doc/installation/build_opts.md | 38 ++++++++ doc/installation/dependencies.md | 130 +++++++++++++++++++++++++ doc/installation/installation.md | 151 ++++++++++++++++++++++++++++++ doc/{ => usage}/debug.md | 17 ++-- doc/{ => usage}/ensembles.md | 36 ++++--- doc/{ => usage}/indices.md | 3 +- doc/{ => usage}/running_aether.md | 119 ++++++++++++++++------- 9 files changed, 474 insertions(+), 171 deletions(-) create mode 100644 doc/README.md delete mode 100644 doc/installation.md create mode 100644 doc/installation/build_opts.md create mode 100644 doc/installation/dependencies.md create mode 100644 doc/installation/installation.md rename doc/{ => usage}/debug.md (89%) rename doc/{ => usage}/ensembles.md (86%) rename doc/{ => usage}/indices.md (93%) rename doc/{ => usage}/running_aether.md (60%) diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..f516b145 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,37 @@ +# Temporary Aether Documentation + +As the [full Aether +documentation](https://aetherdocumentation.readthedocs.io/en/latest/index.html) +is still in [development](https://github.com/AetherModel/AetherDocumentation), +this collection of Markdown files may provide more information on the use of the +Aether model. + +Users are encouraged to begin with the [README](../README.md) at the root of the +repository first, then consult the documents here for further assistance. While +effort was devoted to covering as much as possible regarding setup, it is +inevitable that there will be gaps in the documentation. Do not hesitate to +file an Issue on the Aether repository to request the documentation be filled +in. + +## Quick Links + +Contents within `doc`: + +`doc` + +- Design + - [Standards](design/standards.md) +- Installing + - [Installation](installation/installation.md) + - [Dependencies](installation/dependencies.md) + - [Compilation Options](installation/build_opts.md) +- Usage + - [Running Aether](usage/running_aether.md) + - [Debugging](usage/debug.md) + - [Ensembles](usage/ensembles.md) + - [Indices](usage/indices.md) +- [Doxyfile](Doxyfile) +- [README (this page)](README.md) +- [Citations](citations.md) +- [Dev Team](dev_team.md) +- [Students](student.md) diff --git a/doc/installation.md b/doc/installation.md deleted file mode 100644 index 6f27df2e..00000000 --- a/doc/installation.md +++ /dev/null @@ -1,114 +0,0 @@ -# Installation instructions - -## Dependencies - -Before beginning Aether installation, make sure the following dependencies are -available on your system. We recommend using a package manager, such as -MacPorts, Homebrew (Mac OS X), apt-get (Ubuntu), or Chocolatey (Windows). - -| Mac OS X | -|--------------------------------| -| Dependency | Tested version | -|---------------|----------------| -| armadillo | 11.4 | -| boost | 1.76 | -| cmake | 2.24 | -| gcc | 10, 11 | -| netcdf | 4.9 | -| netcdf-cxx4 | 4.3 | -| nlohmann-json | 3.11 | -| OpenBLAS | 0.3 | -| mpich | 4.1 | - - - -| Ubuntu | -|-------------------------------------| -| Dependency | Tested version | -|--------------------|----------------| -| cmake | 2.24 | -| gcc | 10, 11, 12 | -| libarbadillo-dev | | -| libblas-dev | | -| libboost-dev | | -| liblapack-dev | | -| libnetcdf-dev | | -| libnetcdf-c++4-dev | | -| libopenmpi-dev | | -| nlohmann-json3-dev | | -| openmpi-bin | same as gcc | - -## Get Aether - -Clone Aether from GitHub into a local directory of your choosing. We currently -recommend checking out the `develop` branch, as the model is still awaiting its -first release. - -## Build Aether - -Start by creating a build directory for the necessary files. In this example, -we show how to do so from the Aether project directory and assume you start -there. - -```bash -mkdir build -cd build -``` - -Next, use `cmake` to create the necessary make files in the build directory - -```bash -cmake .. -``` - -This is also the point where compilation options can be chosen. These take -the structure: - -```bash -cmake -DFLAG=VALUE .. -``` - -where `FLAG` is a flag name and `VALUE` is the desired value. A table of model -options is included below. Depending on your system, you may also need to -either declare local environment variables or define them using this flag -system. These will show up as cmake warnings. - -| Aether Flag | Value | Description | -|----------------------|-------|-----------------------------------| -| USE_DOUBLE_PRECISION | Y | Run Aether with higher precission | - -If your default compiler isn't a GCC compiler, you will likely need to specify -the desired GCC compiler at this step using: - -```bash -cmake -DCMAKE_CXX_COMPILER= -``` - -Finally, run the make command in the build directory: - -```bash -make [-jN] -``` - -The `-j` flag tells the computer the number of processers to use when running -the code (where N is that number). To use a single processor, just do not use -the flag. - -If the make command fails, try using the `VERBOSE=1` flag. This will include -additional output that may make it easier to debug the process. - -## Test the executable - -To test the model executable, exit the build diretory and run the test script: - -```bash -cd .. -cp -R share/run ./run.test -cd run.test -./aether -``` - -The output should LOOK LIKE THIS - -For more details about running Aether, see the documentation about creating and -modifying input files in the document running_aether.md. \ No newline at end of file diff --git a/doc/installation/build_opts.md b/doc/installation/build_opts.md new file mode 100644 index 00000000..78e8bb48 --- /dev/null +++ b/doc/installation/build_opts.md @@ -0,0 +1,38 @@ +# Compilation Options + +When compiling the code, you are able to set variables to pass to the compiler. +This can enable things like NetCDF optputs, for example. This page serves to +give a rough description of the available compilation flags and their purpose. + +Depending on your system, you may also need to either declare local environment +variables or define them using this flag system. These will show up as `cmake` +warnings. + +## Table of Compilation Options + +The following table lists many of the optional compilation flags. For boolean +flags, the value can be set to `ON` or `Y` to enable it. All boolean options are +disabled by default. + +| Compilation Flag | Type | Description | +| ---------------------- | ------- | ---------------------------------------- | +| `USE_DOUBLE_PRECISION` | boolean | Run Aether with higher precision | +| `CMAKE_CXX_COMPILER` | path | Path to gcc or MPI executable | +| `USE_FORTRAN` | boolean | Enables the compilation of Fortran code. | +| `USE_NETCDF` | boolean | Write outputs to NetCDF format? | + +Recall, the flags are specified with `cmake -DFLAG=VALUE ..`, where `FLAG` is a +flag name and `VALUE` is the desired value. + +### Additional Test flags + +The following flags are used for testing vaious aspects of the Aether model. +These are not recommended to be enabled unless you are involved in development. + +- `TEST_INTERPOLATION` +- `TEST_COORD` +- `TEST_EXCHANGE` + - Enables `USE_DOUBLE_PRECISION` as well. +- `TEST_GRADIENT` + +> These flags are mutually exclusive; multiple *cannot* be used at the same time. diff --git a/doc/installation/dependencies.md b/doc/installation/dependencies.md new file mode 100644 index 00000000..c02dcd5a --- /dev/null +++ b/doc/installation/dependencies.md @@ -0,0 +1,130 @@ +# Installing Dependencies + +Instructions for MacOS are written for use with MacPorts, as the development +team has mostly used MacPorts and not Homebrew. Commands for the two package +managers are nearly identical, although Homebrew may be preferred if you do not +have root access on your machine. For example, the following two commands will +both install `cmake`. Translating the following guide from MacPorts to Homebrew +is left to the user. + +```bash +sudo port install cmake +brew install cmake +``` + +> To check if any of the following dependencies are already installed on your +system, you will need access to the command line (terminal). To check if (and +where) `cmake` is installed, for example, the command `which cmake` can be run. +If a path is printed, `cmake` is installed. To check the version, run `cmake +--version`. + +The layout of this page is as follows: + +- [Installing Dependencies](#installing-dependencies) + - [Install gcc](#install-gcc) + - [Install cmake](#install-cmake) + - [Install JSON libraries](#install-json-libraries) + - [Install Armadillo (and boost)](#install-armadillo-and-boost) + - [Install NetCDF (optional)](#install-netcdf-optional) + +## Install gcc + +This comes installed by default on Ubuntu. On MacOS this can be installed, for +example, using: + +```bash +sudo port install gcc11 +``` + +> As development began, gcc11 was the latest version; there are newer versions +> of `gcc` available now (latest version is gcc14), which have not yet been +> validated. + +## Install cmake + +Aether uses [CMake](https://cmake.org/) instead of `make`. If you don't have it +installed, you need it. + +For MacOS, this can be installed with: + +```bash +sudo port install cmake +``` + +For Ubuntu/Debian Linux: + +```bash +sudo apt install cmake +``` + +This can be done on RedHat using yum also. + +## Install JSON libraries + +Aether uses the nlohman json package for reading and writing json files. + +On Ubuntu: + +```bash +sudo apt-get install -y nlohmann-json3-dev +``` + +On Mac: + +```bash +sudo port install nlohmann-json +``` + +## Install Armadillo (and boost) + +Simplistically, Armadillo is a system that allows matrix math to be done in C++ +easily. We mostly use it for doing math with matrices (like multiplication, +addition, etc.), but it is much more powerful than this. You will notice that +there are not many 3D loops in Aether, which is due to Armadillo. To make this +all fast, it is best to install the `lapack` and `blas` libraries too. + +On Ubuntu: + +```bash +sudo apt-get install liblapack-dev +sudo apt-get install libblas-dev +sudo apt-get install libboost-dev +sudo apt-get install libarmadillo-dev +sudo apt-get install openmpi-bin libopenmpi-dev +``` + +On Mac: + +```bash +sudo port install lapack +sudo port install OpenBLAS +sudo port install boost +sudo port install armadillo +sudo port install openmpi-bin libopenmpi-dev + ``` + +## Install NetCDF (optional) + +We have removed the strict dependency for NetCDF, but a lot of codes used +NetCDF, so it doesn't hurt to try to install the libraries. Aether uses the +NetCDF library (netcdf-cxx4). As above, NetCDF can be installed using a package +manager. + +On Mac, if you want the clang compiled version of netcdf, then: + +```bash +sudo port install netcdf-cxx4 +``` + +If you want the gcc version of netcdf, then: + +```bash +sudo port install netcdf-cxx4 +gcc10 +``` + +On Ubuntu, gcc is the default compiler, it seems like you can probably just do: + +```bash +sudo apt-get install libnetcdf-dev +sudo apt install libnetcdf-c++4-dev +``` diff --git a/doc/installation/installation.md b/doc/installation/installation.md new file mode 100644 index 00000000..bec10985 --- /dev/null +++ b/doc/installation/installation.md @@ -0,0 +1,151 @@ +# Installation instructions + +Before installing the Aether model, it is recommended to ensure the required +[dependencies](#dependencies) for your system are met. The next step is to +[download](#get-aether) and then [build](#build-aether) the software. Your +install can then be [tested](#test-the-executable). + +## Dependencies + +The Aether development team has tested several possible configurations of the +following dependencies. Note that other versions of these programs may possibly +work, however the listed versions are the recommended starting point. + +If you are working on an HPC cluster, it is likely that all of these +dependencies will already be installed. Consult the documentation of your +specific system to find out how to load the software, which will likely be done +with `module`. + +### MacOS + + | Dependency | Tested version | + |---------------|----------------| + | armadillo | 11.4 | + | boost | 1.76 | + | cmake | 2.24 | + | gcc | 10, 11 | + | netcdf | 4.9 | + | netcdf-cxx4 | 4.3 | + | nlohmann-json | 3.11 | + | OpenBLAS | 0.3 | + | mpich | 4.1 | + + MacOS has two predominant package managers: [Homebrew](https://brew.sh) and + [MacPorts](https://www.macports.org/). Either will work. + +### Linux + + | Dependency | Tested version | + |--------------------|----------------| + | cmake | 2.24 | + | gcc | 10, 11, 12 | + | libarbadillo-dev | | + | libblas-dev | | + | libboost-dev | | + | liblapack-dev | | + | libnetcdf-dev | | + | libnetcdf-c++4-dev | | + | libopenmpi-dev | | + | nlohmann-json3-dev | | + | openmpi-bin | same as gcc | + +The specific package manager to use depends on which distribution of Linux +Aether is installed on. More details on Linux package managers can be found +[here](https://www.linode.com/docs/guides/linux-package-management-overview/#comparison-of-package-managers). + +> Programs such as `conda` and `snap` are **not** recommended to install +> dependencies. + +## Get Aether + +Clone Aether from GitHub into a local directory of your choosing. We currently +recommend checking out the `develop` branch, as the model is still awaiting its +first release. + +```bash +cd [some/directory] +git clone git@github.com:AetherModel/Aether.git +cd Aether +git checkout develop +``` + +## Build Aether + +Start by creating a build directory for the necessary files. In this example, +we show how to do so from the Aether project directory and assume you start +there. + +```bash +mkdir build +cd build +``` + +Next, use `cmake` to create the necessary make files in the build directory + +```bash +cmake .. +``` + +This is also the point where compilation options can be chosen. These take +the structure: + +```bash +cmake -DFLAG=VALUE .. +``` + +Here `FLAG` is a flag name and `VALUE` is the desired value (note the `-D`). A +more complete discussion of the available compilation flags can be found on the +[Compilation Options](build_opts.md) page. + +If your default compiler isn't a GCC compiler, you will likely need to specify +the desired GCC compiler at this step using: + +```bash +cmake -DCMAKE_CXX_COMPILER= +``` + +Finally, run the make command in the build directory: + +```bash +make [-jN] +``` + +The `-j` flag tells the computer the number of processers to use when running +the code (where N is that number). To use a single processor, just do not use +the flag. + +If the make command fails, try using the `VERBOSE=1` flag. This will include +additional output that may make it easier to debug the process. + +## Test the executable + +To test the model executable, exit the build diretory and run the test script: + +```bash +cd .. +cp -R share/run ./run.test +cd run.test +./aether +``` + +The output should look like this: + +```bash +> Need to NOT adjust F10.7, but that isn't included yet!!! +> Writing file : 3DALL_20110320_000000 +> Writing file : 3DBFI_20110320_000000 +> Wall Time : 4s (left : 1111h); Current Time : 2011 3 20 0 0 0 0 +> Wall Time : 4s (left : 23m); Current Time : 2011 3 20 0 0 10 0 +> Wall Time : 5s (left : 14m); Current Time : 2011 3 20 0 0 20 0 +> Wall Time : 5s (left : 9m); Current Time : 2011 3 20 0 0 30 0 +> Wall Time : 5s (left : 7m); Current Time : 2011 3 20 0 0 40 0 +> Wall Time : 6s (left : 7m); Current Time : 2011 3 20 0 0 50 0 +> Wall Time : 6s (left : 5m); Current Time : 2011 3 20 0 1 0 0 +> Wall Time : 6s (left : 5m); Current Time : 2011 3 20 0 1 10 0 +> Wall Time : 7s (left : 5m); Current Time : 2011 3 20 0 1 20 0 +> Wall Time : 7s (left : 4m); Current Time : 2011 3 20 0 1 30 0 +etc. +``` + +For more details about running Aether, see the documentation about creating and +modifying input files in the document running_aether.md. diff --git a/doc/debug.md b/doc/usage/debug.md similarity index 89% rename from doc/debug.md rename to doc/usage/debug.md index 9da04050..d2b5c706 100644 --- a/doc/debug.md +++ b/doc/usage/debug.md @@ -1,14 +1,17 @@ +# Debug The Debug command in the input file sets the type and amount of information that is fed to the user. An example of the Debug command is: +```json "Debug" : { - "iVerbose" : 0, - "dt" : 60.0, - "TimingPercent" : 1.0, - "iTimingDepth" : 5, - "iProc" : 0}, + "iVerbose" : 0, + "dt" : 60.0, + "TimingPercent" : 1.0, + "iTimingDepth" : 5, + "iProc" : 0}, +``` The "iVerbose" command can be used under "Debug" in aether.json to set the overall verbose level in the code. This sets the amount of @@ -37,6 +40,7 @@ certain functions by specifying the function names and the corresponding verbose levels using the "iFunctionVerbose" command as follows: +```json "Debug" : { "iVerbose" : 0, "iFunctionVerbose" : { @@ -44,8 +48,9 @@ follows: "func2" : 2}, "dt" : 10.0 } +``` When a sub-function is entered, the verbose level stays the same if the verbose level for that sub-function is not specified. When that sub-function exits, the verbose level will accordingly be set back to -that of the function it is returning to. \ No newline at end of file +that of the function it is returning to. diff --git a/doc/ensembles.md b/doc/usage/ensembles.md similarity index 86% rename from doc/ensembles.md rename to doc/usage/ensembles.md index 7184c291..54eba907 100644 --- a/doc/ensembles.md +++ b/doc/usage/ensembles.md @@ -1,10 +1,13 @@ +# Ensembles Aether is capable of running with ensembles, meaning that you can run multiples of the same simulation in one run. This is enabled by adding to the aether.json file: +```json "Ensembles" : { "nMembers" : N}, +``` where N is the number of members that you would like to run. In order to do this, the number of nodes that are requested have to be N times @@ -19,21 +22,22 @@ with. To perturb indices in Aether, you can use the "Perturb" command in the aether.json file. As an example: - "Perturb": { - "f107" : { "Mean" : 1.0, - "Std" : 0.10, - "Add" : false, - "Constant" : true}, - "f107a" : { "Mean" : 1.0, - "Std" : 0.10, - "Add" : false, - "Constant" : true}, - "imfbz" : { "Mean" : 0.0, - "Std" : 2.0, - "Add" : true, - "Constant" : false} - +```json +"Perturb": { + "f107" : { "Mean" : 1.0, + "Std" : 0.10, + "Add" : false, + "Constant" : true}, + "f107a" : { "Mean" : 1.0, + "Std" : 0.10, + "Add" : false, + "Constant" : true}, + "imfbz" : { "Mean" : 0.0, + "Std" : 2.0, + "Add" : true, + "Constant" : false} } +``` This perturbs the F10.7, F10.7a, and IMF Bz indices. The sub-parts of the command include: @@ -74,8 +78,10 @@ the post processing code can create mean and std files. You can also perturb chemical reaction rates, but doing something like: +```json "Perturb": { - "Chemistry" : ["R2", "R10", "R20"] } + "Chemistry" : ["R2", "R10", "R20"] } +``` This will perturb the reaction rates in the chemistry file. The Rxx names have to be in the "name" column. The percentage of uncertainty diff --git a/doc/indices.md b/doc/usage/indices.md similarity index 93% rename from doc/indices.md rename to doc/usage/indices.md index 5949ea93..8dc2e20f 100644 --- a/doc/indices.md +++ b/doc/usage/indices.md @@ -1,5 +1,5 @@ -Supported Indices within Aether (with internal id): +# Supported Indices within Aether (with internal id) F10.7: Solar index (f107) @@ -26,4 +26,3 @@ AE: Auroral electrojet index in nT (ae) AU: Auroral electrojet upper index in nT (au) AL: Auroral electrojet lower index in nT (al) - diff --git a/doc/running_aether.md b/doc/usage/running_aether.md similarity index 60% rename from doc/running_aether.md rename to doc/usage/running_aether.md index 8c2d2a81..af7a9260 100644 --- a/doc/running_aether.md +++ b/doc/usage/running_aether.md @@ -1,25 +1,32 @@ # Running Aether -See the installation.md document for how to install Aether. +This document assumes you have already downloaded and built the Aether model. If +not, you should return to [one](../../README.md) of +[these](../installation/installation.md) pages before continuing. ## The first run Once you have compiled you can run Aether. To remember which runs you're doing, we recommend creating a specific directory for each run. This can be done by copying the default run directory to have a special name, like this: + ```bash cd .. cp -R share/run ./run.first_run ``` -This creates the directory where you will do your run. In that directory is a link to the aether executable and an input file called aether.json. +This creates the directory where you will do your run. In that directory is a +link to the aether executable and an input file called aether.json. You can then run the executable from the directory you created. + ```bash cd run.first_run ./aether ``` + You should see something like: + ```bash run.first_run% ./aether > Need to NOT adjust F10.7, but that isn't included yet!!! @@ -39,6 +46,7 @@ etc. ``` The successful end of this will show a timing summary, similar to: + ```bash >main>advance>Chemistry::calc_chemistry nTimes called : 720 @@ -53,21 +61,38 @@ The successful end of this will show a timing summary, similar to: ## Output Files -Aether outputs to a subdirectory called UA/output. At this time, all processors within the Aether run output their own files and they can be combined using the post processor. +Aether outputs to a subdirectory called UA/output. At this time, all processors +within the Aether run output their own files and they can be combined using the +post processor. ### Blocks -Aether is a block-based code, so the domain is split up into different blocks that hold a given volume (for spherical grids, this is some range of latitude, longitude and altitude; for other grids it could differ). If you asked for (for example) 4 processors, then the Earth would be broken up into 4 different blocks and each output would consist of 4 different blocks. +Aether is a block-based code, so the domain is split up into different blocks +that hold a given volume (for spherical grids, this is some range of latitude, +longitude and altitude; for other grids it could differ). If you asked for (for +example) 4 processors, then the Earth would be broken up into 4 different blocks +and each output would consist of 4 different blocks. ### Ensembles -Aether can run the same simulation multiple times simultaneously with different drivers and internal parameters. The collection of these simulations is called an ensemble, with each individual simulation called a member. All of the ensemble members output to the UA/output directory. See the ensembles.md document to learn more about ensembles. +Aether can run the same simulation multiple times simultaneously with different +drivers and internal parameters. The collection of these simulations is called +an ensemble, with each individual simulation called a member. All of the +ensemble members output to the UA/output directory. See the ensembles.md +document to learn more about ensembles. ### Post processing -Because there can be multiple blocks and multiple ensemble members, there is a post processor that can pull all of the files together. By default, the post processor combines all of the block files for one ensemble member for one time into one file. This means that if there are 25 hourly outputs requested, there will be 25 total files for each ensemble member. +Because there can be multiple blocks and multiple ensemble members, there is a +post processor that can pull all of the files together. By default, the post +processor combines all of the block files for one ensemble member for one time +into one file. This means that if there are 25 hourly outputs requested, there +will be 25 total files for each ensemble member. -If there is more than one simulation, then the post processor will create a mean file for each time. If there are more than two ensemble members, a standard deviation file is also created. The mean and standard deviations are calculated across ensemble members. +If there is more than one simulation, then the post processor will create a mean +file for each time. If there are more than two ensemble members, a standard +deviation file is also created. The mean and standard deviations are calculated +across ensemble members. To run the post processor, do the following: @@ -76,54 +101,72 @@ cd UA/output ../../../srcPython/post_process.py ``` -By default, this will also leave the raw files and will produce plots just to make sure that everything is working ok. If you don't need the raw files (there is not a good reason to keep these, unless you are debugging the post processor), you can run the post-processor with the "-rm" argument. If you don't want any plots, you can run with the "-alt=-1" argument. An example of this is (this is how it is often run): +By default, this will also leave the raw files and will produce plots just to +make sure that everything is working ok. If you don't need the raw files (there +is not a good reason to keep these, unless you are debugging the post +processor), you can run the post-processor with the "-rm" argument. If you +don't want any plots, you can run with the "-alt=-1" argument. An example of +this is (this is how it is often run): ```bash ../../../srcPython/post_process.py -rm -alt=-1 ``` -If you are going to be using Aether a lot, you may want to copy the post-processor into your bin directory. +If you are going to be using Aether a lot, you may want to copy the +post-processor into your bin directory. ## Input Files -Aether reads in a bunch of files, most of which are specified by the settings file. In order to minimize the number of places where new settings need to be specified, Aether uses a defaults file that sets the generic defaults of the model. This file is in UA/inputs/defaults.json. +Aether reads in a bunch of files, most of which are specified by the settings +file. In order to minimize the number of places where new settings need to be +specified, Aether uses a defaults file that sets the generic defaults of the +model. This file is in UA/inputs/defaults.json. ## defaults.json file -This is a json file that sets all of the defaults within Aether. This file should never be modified! +This is a json file that sets all of the defaults within Aether. This file +should never be modified! ### For Developers -Within Aether, the inputs.cpp file has a large handful of of get_ routines to get the values of the settings that the user has set. +Within Aether, the inputs.cpp file has a large handful of of get_ routines to +get the values of the settings that the user has set. ## aether.json file -The file aether.json is read in AFTER the defaults file and these settings overwrite the defaults. So, if you want to modify the default settings, simply copy a setting out of defaults.json and paste it into aether.json. Then, you can modify the setting in aether.json. +The file aether.json is read in AFTER the defaults file and these settings +overwrite the defaults. So, if you want to modify the default settings, simply +copy a setting out of defaults.json and paste it into aether.json. Then, you +can modify the setting in aether.json. -Because the files are json files, you don't actually have to set all of the subsettings within a specific setting. For example, within the defaults.json file, the EUV setting is: +Because the files are json files, you don't actually have to set all of the +subsettings within a specific setting. For example, within the defaults.json +file, the EUV setting is: ```bash "Euv" : { - "doUse" : true, - "Model" : "euvac", - "File" : "UA/inputs/euv.csv", - "IncludePhotoElectrons" : true, - "HeatingEfficiency" : 0.05, - "dt" : 60.0}, + "doUse" : true, + "Model" : "euvac", + "File" : "UA/inputs/euv.csv", + "IncludePhotoElectrons" : true, + "HeatingEfficiency" : 0.05, + "dt" : 60.0}, ``` -If you simply want to turn off the EUV, you can insert this into the aether.json file: +If you simply want to turn off the EUV, you can insert this into the aether.json +file: ```bash "Euv" : { - "doUse" : false}, + "doUse" : false}, ``` -or, if you wanted to use the 59 wavelength bins instead of the 37 EUVAC bins, you could do: +or, if you wanted to use the 59 wavelength bins instead of the 37 EUVAC bins, +you could do: ```bash "Euv" : { - "File" : "UA/inputs/euv_59.csv"}, + "File" : "UA/inputs/euv_59.csv"}, ``` ## planet.in file @@ -155,12 +198,13 @@ CO2, 46, 7, 3.6e-4, 0.69, 0, 4.5e15 ``` This is a comma seperated list that includes: + - name - this is the common name of the species - mass - the atomic mass of the species - vibration - the degrees of freedom for the species - thermal_cond - the thermal conductivity coeficient of the species - thermal_exp - the exponent that is put on the temperature (Lambda = A * T^B) -- advect - whether the species is advected or not +- advect - whether the species is advected or not - BC - the lower boundary condition on the species density ```bash @@ -177,10 +221,11 @@ O+_2P, 16, 1, 0 ``` This is a comma separated list that includes: + - name - this is the common name of the species - mass - the atomic mass of the species - charge - the charge of the species -- advect - whether the species is advected or not +- advect - whether the species is advected or not ```bash #TEMPERATURE @@ -194,30 +239,36 @@ Within the input file (i.e., aether.json), the following can be set: ```bash "InitialConditions" : { - "type" : "Planet"}, - + "type" : "Planet"}, + "BoundaryConditions" : { - "type" : "Planet"}, + "type" : "Planet"}, ``` -If these are set to "Planet", then the temperature profile is set as the initial condition. If the boundary condition is set to "Planet", then the lowest altitude is used as a boundary condition on the temperature. +If these are set to "Planet", then the temperature profile is set as the initial +condition. If the boundary condition is set to "Planet", then the lowest +altitude is used as a boundary condition on the temperature. ## orbits.csv file -This file contains all of the orbital, mass, rotation, and magnetic field characteristics of the planets. All of the planets within the solar system are included. Other planets (like exo-planets or artificial planets) can be included by adding lines. +This file contains all of the orbital, mass, rotation, and magnetic field +characteristics of the planets. All of the planets within the solar system are +included. Other planets (like exo-planets or artificial planets) can be +included by adding lines. ## chemistry file The chemistry file defines all of the chemical reactions within the system. For example: + ```bash He+ + e- -> He, with R = 4.8e-18 * (250 / Te) ^ 0.7 ``` -With an exothermal energy of xxx. Also, the uncertainty of the reaction can be set too (set to 10%, or 0.1). + +With an exothermal energy of xxx. Also, the uncertainty of the reaction can be +set too (set to 10%, or 0.1). ```bash R11,He+,e-,,,He,,,4.80E-18,(250/Te)^0.7,,1,0,,0.1,,,,gitm,250,Te,0.7,,,,1, ``` - - From c26b79efdb81e2548ca324190e7fae31fee2e27d Mon Sep 17 00:00:00 2001 From: "abukowski21@gmail.com" Date: Wed, 24 Jul 2024 14:26:17 -0400 Subject: [PATCH 105/127] DOC: Create folder doc/internal/, style markdown files Moving the files grid.md, ensembles.md, indices.md to new folder `docs/internal`. Should organize things a little better, but the folder name might need to be changed. ALSO: - Update the README's with the updated TOC's. - Consistent Markdown syntax: - consistent use of dashes/equals & hashtags for header level - no double empty lines - headings in descending-by-one order - denote links with <>'s - Add a language to each fenced (three backticks) code blocks --- CONTRIBUTING.md | 45 ++-- README.md | 2 +- doc/README.md | 6 +- doc/design/standards.md | 3 +- doc/grid.md | 153 ------------ doc/{usage => internals}/ensembles.md | 0 doc/internals/grid.md | 332 ++++++++++++++++++++++++++ doc/{usage => internals}/indices.md | 0 8 files changed, 365 insertions(+), 176 deletions(-) delete mode 100644 doc/grid.md rename doc/{usage => internals}/ensembles.md (100%) create mode 100644 doc/internals/grid.md rename doc/{usage => internals}/indices.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 141db86d..07ced9ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,12 +1,21 @@ -Contributing -============ - -Code ----- +# Contributing Please read the [standards document](doc/design/standards) before contributing. +- [Contributing](#contributing) + - [Code](#code) + - [Development](#development) + - [Using AStyle](#using-astyle) + - [Linting](#linting) + - [Commit Styling](#commit-styling) + - [Pull Requests](#pull-requests) + - [Issues](#issues) + +--- + +## Code + ### Development Make new branches for features `git checkout -b my_feature` and commit often @@ -16,8 +25,8 @@ you have something that works. #### Using AStyle We have started using astyle to format the code. Please see -http://astyle.sourceforge.net/. There is a style file in the root directory -of Aether, called .astylerc. To run this, do: +. There is a style file in the root directory of +Aether, called .astylerc. To run this, do: AStyle --project=.astylerc src/*.cpp @@ -25,7 +34,6 @@ on WSL with Ubuntu, the command seems to be: astyle --options=.astylerc src/*.cpp - #### Linting For *C++* code make sure to use a static code checker like @@ -44,13 +52,13 @@ python3 -m pip cpplint python3 -m pip --user cpplint ``` -Using a linter in an editor is a good supplement, but not a replacement for the static linters. -The linter in the 'atom' editor requires that you install the `linter` and `gcc-linter` packages. -Atom also has additional packages `whitespaces` and `tabs-to-spaces` -to automatically remove whitespaces at the end of the lines, and -convert tabs to spaces. +Using a linter in an editor is a good supplement, but not a replacement for the +static linters. The linter in the 'atom' editor requires that you install the +`linter` and `gcc-linter` packages. Atom also has additional packages +`whitespaces` and `tabs-to-spaces` to automatically remove whitespaces at the +end of the lines, and convert tabs to spaces. -### Commit Styling +## Commit Styling The first line of the commit must be *at most* ~50 characters long and should start with either. @@ -74,25 +82,24 @@ For example, *do:* -``` +```github FEAT: Hydrostatic density implementation. ``` *don't:* -``` +```github Implemented hydrostatic density. (feature) ``` -### Pull Requests +## Pull Requests Make sure you have linted and checked your code before asking for a pull request. Before requesting a review, ensure the pull request check list has been completed. Another member must check the code and approve it before merge. -Issues ------- +## Issues *Issues* are reporting bugs, feature requests, or goals for the project. In order to submit an issue make sure it follows the [issue diff --git a/README.md b/README.md index b28a8007..d6fd6ebe 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ When you are in a run directory, they are: 1. UA/inputs/defaults.json. These set the default inputs for the run and should not be modified at all. You can look at these and copy the settings that you -want to change to this file: +want to change to this file. 2. aether.json. This file can and should be modified to direct the code to run the way that you would like. You can copy settings from the default.json file diff --git a/doc/README.md b/doc/README.md index f516b145..d1601e3d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -28,8 +28,10 @@ Contents within `doc`: - Usage - [Running Aether](usage/running_aether.md) - [Debugging](usage/debug.md) - - [Ensembles](usage/ensembles.md) - - [Indices](usage/indices.md) +- Aether Internals + - [Ensembles](internals/ensembles.md) + - [Indices](internals/indices.md) + - [The Grid](internals/grid.md) - [Doxyfile](Doxyfile) - [README (this page)](README.md) - [Citations](citations.md) diff --git a/doc/design/standards.md b/doc/design/standards.md index af071cc4..a7422db1 100644 --- a/doc/design/standards.md +++ b/doc/design/standards.md @@ -4,7 +4,8 @@ Aether Coding and Design Standards Document Overall Design Philosophy ------------------------- -## Files +Files +---- Directory Structure: diff --git a/doc/grid.md b/doc/grid.md deleted file mode 100644 index b7b76eb3..00000000 --- a/doc/grid.md +++ /dev/null @@ -1,153 +0,0 @@ - -# Grids in Aether - -Aether uses a 2d domain decomposition and the grid system is basically a 3D i, j, k system, meaning that the arrays within Aether are 3D arrays. Aether decomposes the grid in the first 2 dimensions (i and j) using a quadtree structure, while the 3rd dimension is left alone and each processor solves for the entire 3rd dimension. - -Practically, what this means is that Aether uses powers of 4 to specify the grid system. When you ask for 4x the number of processors it doubles the resolution in i and j. You can't double in i or j independently. - -Aether using root nodes, which specify the smallest number of processors that can be run on. For example, the simple "Sphere" grid has one root node that handles the entire Earth (in latitude and longitude). If the resolution needs to be doubles, 4 processors can be asked for. If the resolution is doubled again, 16 processors are needed. Etc. However, in the altitude/radial direction, the number of points that are specified in the aether.json is never doubled when more processors are asked for. - -## Grid Types Explained - -Aether has two types of grid systems - the neutral grid (neuGrid) and the ion grid (ionGrid). For each type of constituent (neutral or ion), their primary grid is the one where most of the equations are solved, and then they are passed to the other grid. For example, the neutral winds are solved for on the NeuGrid, and then passed onto the IonGrid in order to calculate source terms for the ions. As another example, the ion advection is solved for on the ion grid. The ion densities are then passed to the neutral grid, where the source terms for the neutrals are calculated. - -These grids can be identical or nearly identical. If they are, then it is best to have them on a neutral type of grid, since the stability of the neutrals along the 3rd dimension (where gravity is prime) is hard to achieve. - -The neutral grid system typically has its third axis aligned (mostly) with the radial direction. This is to allow special solvers to treat gravity and the gradient in pressure in a special way. There are two issues with solving the neutrals in the third dimension: (1) often, the top of thermosphere models are supposed to be the exosphere, which means that they can't extend too far in the vertical direction; and (2) a problem with neutral solvers that can solve the full momentum equation in the third dimension is that they struggle with having too many scale heights in a domain. These limit the full height of the model domain for the neutrals. - -For the ions, with systems that have magnetic fields, the plasma often moves up fieldlines on the dayside and down fieldlines on the nightside. This transport is often above the top of the neutral grid. Further, the ions are often structured by the magnetic field, making this the natural coordinate system. For planets without magnetic fields, a grid similar to the neutrals may be useful. The ion grid can extend above and below the neutral grid with both a magnetic-field-aligned grid and a spherical grid. - -## Grid Shapes Explained - -Aether currently has three basic grid shapes: spherical, cubesphere, and dipole. The spherical grid is a (i, j, k) = (longitude, latitude, altitude) system, with these being (mostly) orthogonal to each other. This grid system can simulate a sub-region of the Earth if desired. - -### TL;DR - -The user needs to specify the shape of the grid, which specifies the grid shape and the number of root nodes. Shapes include: sphere (1 root node), sphere6 (6 root nodes), cubesphere (6 root nodes), dipole (1 root node), dipole4 (4 root nodes), and dipole6 (6 root nodes). - -### The Sphere Grid - -The sphere grid is a normal longitude, latitude, altitude grid. - -### The Cubesphere Grid - -The cubesphere grid is composed of 6 different faces, similar to a cube, but where each cube "face" is pushed out to form a sphere. The corners of the cube intersect the sphere, and all of the other grid points on the cube are pushed out to form the cube. One cube face defines the southern polar region, one face defines the northern polar region, and the other four faces are spaced in longitude around the equatorial region. For the cubesphere grid, the (k) dimension is altitude. The (i, j) system is set up so that (i) is considered left-right on the cube face, while (j) is up-down. For the four faces around the equator, (i, j) is roughly (lon, lat), but not exactly. For the polar faces, the relationship between (i, j) and longitude, latitude is much more complex. - -For both the spherical grid and the cubesphere grid, the altitudes (k) can be stretched or uniform. A lower boundary is set and the delta-altitude is specified as either a constant distance or a constant percentage of the bulk scale-height. - -### The Dipole Grid - -The dipole grid is aligned with the magnetic field. The (k) dimension is along the fieldline, (i) is magnetic longitude, and (j) is roughly latitude for the bottom of the field-line. Each field-line starts at the lowest altitude and curves towards the equator. In the northern hemisphere, this means that the field-lines curve south, while in the southern hemisphere, they curve north. The latitudinal spacing is such that there is a dependence on the L-shell (i.e., the equatorial radial extent of the field-line). Along the (k) dimension, field-lines either terminate when they reach the equatorial plane, forming half of a full field-line or they terminate at the highest point specified in the aether.json file. Any grid point with an L-shell less than the peak altitude will terminate in the equatorial plane, while any field-line that has an L-shell above the peak altitude will simply terminate. Field-lines that terminate in the equatorial plane have corresponding field-lines in the other hemisphere, so ghostcells are used to pass information back and forth. Field-lines that terminate at the maximum altitude have vertical boundary conditions set in the ghost cells. - -The transition from the "closed" field-line region to the "open" field-line region is a natural break point in the grid. The transition between these regions can be handled with ghostcells in the "latitudinal" direction. Therefore it makes the most sense to have have 4 distinct regions in "latitude": south open, south closed, north closed, north open. The message passing is treated differently at the boundaries between each of these regions. - -### Root Nodes - -A fundamental assumption within Aether is that each processor does computation on one and only one block. This means that each processor does not deal with multiple blocks, and therefore the distribution of blocks across processors has to match exactly. This document uses the words "block" and "node" somewhat interchangably. Technically, a "block" is single (i, j, k) grid, while a "node" can be multiple "blocks" that make up a section of the globe. - -Aether uses a quadtree system to subdivide and distribute the grids (or blocks) across processors. This means that when an additional level of refinement is desired, an individual block is split in 4 - the number of blocks is doubled in both (i) and (j). The question then is - how many blocks to start with? These are the root nodes. - -For the whole globe sphere shape, there is one single root node, which allows users to run the code on a single processor. When a user asks for one processor using the sphere shape, there is only one single block, which is the root node, which spans then entire globe. When the user asks for four processors using the sphere shape, the number of blocks in latitude are doubled and the number of blocks in longitude are doubled. There is still only one root node, but the number of blocks is four, with 2 in the longitudinal direction and 2 in the latitudinal direction. If the user asks for 16 processors using the sphere shape, the blocks are sub-divided again, with sill one single root node, and 16 blocks - four in the longitudinal direction and four in the altitudinal direction. With a sphere grid, the number of processors that can be used to specify the grid are then: 1, 4, 16 (=4^2), 64 (=4^3), 256 (=4^4), 1024 (=4^5), etc. - -With a cubesphere grid, there are six root nodes, meaning that the code needs six processors to run on just to start. Each root node is a face of the cubesphere. If the user asks for 24 processors (i.e., 6 root nodes that are each divided into four blocks each), each root node is split in half along the left-right direction and the up-down direction. For a cubesphere grid, the number of processors that can be used to specify the grid are then: 6, 24 (= 6 * 4), 96 (6 * 4^2), 384 (6 * 4^3), etc. - -The root nodes indicate the span of the grid that they cover. This is done in a header file. In order to accomplish this, the lower-left corner location (ORGINS) is specified as well the span of the root node in the left-to-right (i) direction (RIGHTS) and in the (j) down-to-up direction (UPS). The easiest example is here: -```bash -namespace Sphere { - /// The normalized origins of each face of the cube (i.e. corner) - static const arma_mat ORIGINS = { - { 0.0, -0.5, 0.0} - }; - /// Normalized right steps in cube - static const arma_mat RIGHTS = { - {2.0, 0.0, 0.0} - }; - /// Normalized right steps in cube - static const arma_mat UPS = { - {0.0, 1.0, 0.0} - }; -}; -``` - -Since the sphere goes from -90 deg to +90 deg in latitude, and 0 deg to 360 deg in longitude, and pi is the normalizer, then the grid should go from -0.5 to 0.5 in the UPS direction, so the ORIGIN is placed at -0.5 and the span is 1.0. In the longitudinal direction, the grid should go from 0 - 2, so the ORIGIN is placed at 0.0 and the span is 2.0. - -This could be altered to have two root nodes. If someone wanted the root node to be "square" in that the lat and lon spans are the same, this could be done: -```bash -namespace Sphere2 { - /// The normalized origins of each face of the cube (i.e. corner) - static const arma_mat ORIGINS = { - { 0.0, -0.5, 0.0}, - { 1.0, -0.5, 0.0} - }; - /// Normalized right steps in cube - static const arma_mat RIGHTS = { - {1.0, 0.0, 0.0}, - {1.0, 0.0, 0.0} - }; - /// Normalized right steps in cube - static const arma_mat UPS = { - {0.0, 1.0, 0.0}, - {0.0, 1.0, 0.0} - }; -}; -``` -Notice that the namespace is different, so that it can be unique. In this case, there are two ORGINS (offset by 1.0 in longitude), two RIGHTS (which are the same), and two UPS (which are the same). - -In both of these examples, the third dimension doesn't change. This is because a single altitude in a spherical grid can be fully described with two variables (lat and lon). For a cubesphere grid, on the other hand, there are three variables that are needed - each face is in an XY, XZ, or YZ plane, so all three (X, Y, and Z) are needed. The 6 root nodes for the cubesphere are specified in the cubesphere.h header file. - -## Specifying the Grid - -There are many different components to specifying the actual grid that is desired, namely: -- Min and Max latitude -- Min and Max longitude -- Min Altitude, whether a stretched altitude is desired, and the altitudinal spacing - -In addition, the number of grid points that should be used in each block are specified: -- nLons or nX - number of grid cells per block in the i direction -- nLats or nY - number of grid cells per block in the j direction -- nAlts or nZ - total number of grid cells in the block in the k direction - -### Horizontal Resolution - -For some grid shapes (Sphere and Dipole), the total number of grid cells in the i and j direction can be determined by, for example, multiplying the number of blocks in the i direction by the number of cells in each block in the i direction. So, with a spherical grid with one root node, and 256 processors used, the number of blocks in the i and j direction is (256 = 4 * 4 * 4 * 4. breaking it into both directions - (2*2) * (2*2) * (2*2) * (2*2) or (2 * 2 * 2 * 2) * (2 * 2 * 2 * 2) or 16 x 16) 16 and 16. So, the total number of blocks in the i direction is 16 * nLons and in the j direction is 16 * nLats. - -For the cubesphere grid, the nX and nY are the number of grid cells in the i and j direction. At this time, these have to be identical in order to have the grid cells match up along the boundaries to the top and bottom nodes. The resolution of the Cubesphere grid is roughly 360 deg / (4 * nX * sqrt(nProc/6)). For example, if nX = 18, and 24 processors are requested, then the resolution = 360 / (4 * 18 * sqrt(4)) = 360 / (72 * 2) = 2.5 deg. As another example, to make a grid with 1 deg resolution, with 96 processors, nX would have to be 1 = 360 / (4 * nX * 4) = 22.5 / nX, so nX has to be around 22. (If nX were 22, and nProc = 96, then the resolution would be 1.02 deg). - -### Vertical Resolution - -In all grids, the nAlts or nZ are not parallelized, so the number of points in the k direction is what is specified. For the sphere and cubesphere grids, this is the number of altitude points. On the dipole grid, this is the number of points along the dipole flux tube. - - -```bash - "neuGrid" : { - "Shape" : "sphere", - "LatRange" : [-90.0, 90.0], - "nLatsPerBlock" : 18, - "LonRange" : [0.0, 360.0], - "nLonsPerBlock" : 36, - "nAlts" : 50, - "MinAlt" : 100.0, - "dAltkm" : 5.0, - "dAltScale" : 0.25, - "IsUniformAlt" : true, - "AltFile" : ""}, -``` - -```bash - "ionGrid" : { - "Shape" : "dipole", - "LatRange" : [-90.0, 90.0], - "nLatsPerBlock" : 18, - "LonRange" : [0.0, 360.0], - "nLonsPerBlock" : 36, - "nAlts" : 200, - "MinAlt" : 80.0, - "MinApex" : 120.0, - "MaxAlt" : 5000.0}, -``` - -The dipole grid has both open field-lines and closed field-lines. The closed field-lines are near the equator, while the open field-lines are near the poles. The variable "MaxAlt" sets where this differentiation occurs - if the apex of the field-line is above this altitude, then it is open. All field-lines in Aether start are the "MinAlt" and rise along a dipolar shape until they either encounter the equatorial plane or "MaxAlt". In the south, these field-lines tilt towards the north (from MinAlt to MaxAlt) and in the north, the field-lines tilt towards the south (from MinAlt to MaxAlt). -- The spacing is uniform in longitude. -- The spacing along the field-line has non-uniform spacing. -- The spacing in latitude is non-uniform. - diff --git a/doc/usage/ensembles.md b/doc/internals/ensembles.md similarity index 100% rename from doc/usage/ensembles.md rename to doc/internals/ensembles.md diff --git a/doc/internals/grid.md b/doc/internals/grid.md new file mode 100644 index 00000000..a9858803 --- /dev/null +++ b/doc/internals/grid.md @@ -0,0 +1,332 @@ +# Grids in Aether + +Aether uses a 2d domain decomposition and the grid system is basically a 3D `(i, +j, k)` system, meaning that the arrays within Aether are 3D arrays. Aether +decomposes the grid in the first 2 dimensions (`i` and `j`) using a quadtree +structure, while the 3rd dimension is left alone and each processor solves for +the entire 3rd dimension. + +Practically, what this means is that Aether uses powers of 4 to specify the grid +system. When you ask for 4x the number of processors it doubles the resolution +in `i` and `j`. You can't double in `i` or `j` independently. + +Aether using root nodes, which specify the smallest number of processors that +can be run on. For example, the simple "Sphere" grid has one root node that +handles the entire Earth (in latitude and longitude). If the resolution needs +to be doubles, 4 processors can be asked for. If the resolution is doubled +again, 16 processors are needed, etc. However, in the altitude/radial direction, +the number of points that are specified in the aether.json is unchanged, as it +does not rely on the number of processors used. + +- [Grids in Aether](#grids-in-aether) + - [Grid Types Explained](#grid-types-explained) + - [Grid Shapes Explained](#grid-shapes-explained) + - [TL;DR](#tldr) + - [The Sphere Grid](#the-sphere-grid) + - [The Cubesphere Grid](#the-cubesphere-grid) + - [The Dipole Grid](#the-dipole-grid) + - [Root Nodes](#root-nodes) + - [Sphere](#sphere) + - [Cubesphere](#cubesphere) + - [Specifying Root Nodes](#specifying-root-nodes) + - [Specifying the Grid](#specifying-the-grid) + - [Horizontal Resolution](#horizontal-resolution) + - [Vertical Resolution](#vertical-resolution) + +## Grid Types Explained + +Aether has two types of grid systems - the neutral grid (`neuGrid`) and the ion +grid (`ionGrid`). For each type of constituent (neutral or ion), their primary +grid is the one where most of the equations are solved, and then they are passed +to the other grid. For example, the neutral winds are solved for on the +`neuGrid`, and then passed onto the `ionGrid` in order to calculate source terms +for the ions. As another example, the ion advection is solved for on the +`ionGrid`. The ion densities are then passed to the `neuGrid`, where the source +terms for the neutrals are calculated. + +These grids can be identical or nearly identical. If they are, then it is best +to have them on a neutral type of grid, since the stability of the neutrals +along the 3rd dimension (where gravity is prime) is hard to achieve. + +The neutral grid system typically has its third axis aligned (mostly) with the +radial direction. This is to allow special solvers to treat gravity and the +gradient in pressure in a special way. There are two issues with solving the +neutrals in the third dimension: (1) often, the top of thermosphere models are +supposed to be the exosphere, which means that they can't extend too far in the +vertical direction; and (2) neutral solvers struggle with having too many scale +heights in a domain when solving the full momentum equation. These limit the +full height of the model domain for the neutrals. + +For the ions, with systems that have magnetic fields, the plasma often moves up +field-lines on the dayside and down field-lines on the nightside. This transport +is often above the top of the neutral grid. Further, the ions are often +structured by the magnetic field, making this the natural coordinate system. For +planets without magnetic fields, a grid similar to the neutrals may be useful. +The ion grid can extend above and below the neutral grid with both a +magnetic-field-aligned grid and a spherical grid. + +## Grid Shapes Explained + +Aether currently has three basic grid shapes: `spherical`, `cubesphere`, and +`dipole`. The spherical grid is an (`i`, `j`, `k`) = (longitude, latitude, +altitude) system, with these being (mostly) orthogonal to each other. This grid +system can simulate a sub-region of the Earth if desired. + +### TL;DR + +The user needs to specify the shape of the grid, which specifies the grid shape +and the number of root nodes. Shapes include: `sphere` (1 root node), `sphere6` +(6 root nodes), `cubesphere` (6 root nodes), `dipole` (1 root node), `dipole4` +(4 root nodes), and `dipole6` (6 root nodes). + +### The Sphere Grid + +The sphere grid is a normal longitude, latitude, altitude grid. + +### The Cubesphere Grid + +The cubesphere grid is composed of 6 different faces, similar to a cube, but +where each cube "face" is pushed out to form a sphere. The corners of the cube +are first set to intersect the sphere, then all of the other grid points on the +cube are pushed out until they intersect the sphere. + +| Cube with spherical cube | +|:--:| +| A graphical representation of the cubesphere grid.
(*Source: A2569875, CC BY-SA 4.0 , via Wikimedia Commons*) | + +One cube face defines the southern polar region, one face defines the northern +polar region, and the other four faces are spaced in longitude around the +equatorial region. For the cubesphere grid, the `k` dimension is altitude. The +(`i, j`) system is set up so that `i` is considered left-right on the cube face, +while `j` is up-down. For the four faces around the equator, (`i, j`) is roughly +(longitude, latitude), but not exactly. For the polar faces, the relationship +between (`i, j`) and (longitude, latitude) is much more complex. + +For both the spherical grid and the cubesphere grid, the altitudes, `k`, can be +stretched or uniform. A lower boundary is set and the delta-altitude is +specified as either a constant distance or a constant percentage of the bulk +scale-height. + +### The Dipole Grid + +The dipole grid is aligned with the magnetic field. The `k` dimension is along +the fieldline, `i` is magnetic longitude, and `j` is roughly latitude for the +bottom of the field-line. Each fieldline starts at the lowest modeled altitude +and curves towards the equator. In the northern hemisphere, this means that the +fieldlines curve south, while in the southern hemisphere they curve north. + +The latitudinal spacing is such that there is a dependence on the L-shell (i.e., +the equatorial radial extent of the field-line). Along the `k` dimension, +field-lines either terminate when they reach the equatorial plane, forming half +of a full field-line, or they terminate at the highest point specified in the +aether.json file, forming an open field line. Any grid point with an L-shell +less than the peak altitude will terminate in the equatorial plane, while any +field-line that has an L-shell above the peak altitude will simply terminate. +Field-lines that terminate in the equatorial plane have corresponding +field-lines in the other hemisphere, so ghostcells are used to pass information +back and forth. Field-lines that terminate at the maximum altitude have +vertical boundary conditions set in the ghost cells. + +The transition from the "closed" field-line region to the "open" field-line +region is a natural break point in the grid. The transition between these +regions can be handled with ghostcells in the "latitudinal" direction. +Therefore it makes the most sense to have have 4 distinct regions in "latitude": +south open, south closed, north closed, north open. The message passing is +treated differently at the boundaries between each of these regions. + +### Root Nodes + +>This document uses the words "block" and "node" somewhat +interchangably. Technically, a "block" is single (`i, j, k`) grid, while a +"node" can be multiple "blocks" that make up a section of the globe. + +A fundamental assumption within Aether is that each processor does computation +on one and only one block. This means that each processor does not deal with +multiple blocks, and therefore the distribution of blocks across processors has +to match exactly. + +Aether uses a quadtree system to subdivide and distribute the grids (or blocks) +across processors. This means that when an additional level of refinement is +desired, an individual block is split in 4 - the number of blocks is doubled in +both `i` and `j`. The question then is *how many blocks to start with*? These +are the root nodes. + +#### Sphere + +For the whole globe `sphere` shape, there is one single root node, which allows +users to run the code on a single processor. When a user asks for one processor +using this sphere shape, there is only one single block, which is the root node, +and spans the entire globe. When the user asks for four processors using the +sphere shape, the number of blocks in latitude are doubled and the number of +blocks in longitude are doubled. There is still only one root node, but the +number of blocks is four, with 2 in the longitudinal direction and 2 in the +latitudinal direction. If the user asks for 16 processors using the sphere +shape, the blocks are sub-divided again, with sill one single root node, and 16 +blocks - four in the longitudinal direction and four in the altitudinal +direction. With a sphere grid, the number of processors that can be used to +specify the grid are then: 1, 4, 16 (=4^2), 64 (=4^3), 256 (=4^4), 1024 (=4^5), +etc. + +#### Cubesphere + +With a cubesphere grid, there are six root nodes, meaning that the code needs +six processors to run on just to start. Each root node is a face of the +cubesphere. If the user asks for 24 processors (i.e., 6 root nodes that are each +divided into four blocks each), each root node is split in half along the +left-right direction and the up-down direction. For a cubesphere grid, the +number of processors that can be used to specify the grid are then: 6, 24 (6 +\* 4), 96 (6 \* 4^2), 384 (6 \* 4^3), etc. + +#### Specifying Root Nodes + +The root nodes indicate the span of the grid that they cover. This is done in a +header file. In order to accomplish this, the lower-left corner location +(ORGINS) is specified as well the span of the root node in the left-to-right (i) +direction (RIGHTS) and in the (j) down-to-up direction (UPS). The easiest +example is here: + +```cpp +namespace Sphere { + /// The normalized origins of each face of the cube (i.e. corner) + static const arma_mat ORIGINS = { + {0.0, -0.5, 0.0} + }; + /// Normalized right steps in cube + static const arma_mat RIGHTS = { + {2.0, 0.0, 0.0} + }; + /// Normalized right steps in cube + static const arma_mat UPS = { + {0.0, 1.0, 0.0} + }; +}; +``` + +Since the sphere goes from -90$^\circ$ to +90$^\circ$ in latitude, and 0$^\circ$ +to 360$^\circ$ in longitude, and pi is the normalizer, then the grid should go +from -0.5 to 0.5 in the UPS direction, so the ORIGIN is placed at -0.5 and the +span is 1.0. In the longitudinal direction, the grid should go from 0 to 2, so +the ORIGIN is placed at 0.0 and the span is 2.0. + +This could be altered to have two root nodes. If someone wanted the root node to +be "square", in that the latitude and longitude spans are the same, this could +be done with: + +```cpp +namespace Sphere2 { + /// The normalized origins of each face of the cube (i.e. corner) + static const arma_mat ORIGINS = { + {0.0, -0.5, 0.0}, + {1.0, -0.5, 0.0} + }; + /// Normalized right steps in cube + static const arma_mat RIGHTS = { + {1.0, 0.0, 0.0}, + {1.0, 0.0, 0.0} + }; + /// Normalized right steps in cube + static const arma_mat UPS = { + {0.0, 1.0, 0.0}, + {0.0, 1.0, 0.0} + }; +}; +``` + +Notice that the namespace is different, so that it can be unique. In this case, +there are two ORGINS (offset by 1.0 in longitude), two RIGHTS (which are the +same), and two UPS (which are the same). + +In both of these examples, the third dimension doesn't change. This is because a +single altitude in a spherical grid can be fully described with two variables +(lat and lon). For a cubesphere grid, on the other hand, there are three +variables that are needed - each face is in an XY, XZ, or YZ plane, so all three +(X, Y, and Z) are needed. The 6 root nodes for the cubesphere are specified in +the [cubesphere.h header file](../../include/cubesphere.h). + +## Specifying the Grid + +There are many different components to specifying the actual grid that is +desired, namely: + +- Min and Max latitude +- Min and Max longitude +- Min Altitude, whether a stretched altitude is desired, and the altitudinal + spacing + +In addition, the number of grid points that should be used in each block are +specified: + +- nLons or nX - number of grid cells per block in the i direction +- nLats or nY - number of grid cells per block in the j direction +- nAlts or nZ - total number of grid cells in the block in the k direction + +### Horizontal Resolution + +For some grid shapes (`sphere` and `dipole`), the total number of grid cells in +the `i` and `j` direction can be determined by, for example, multiplying the +*number of blocks* in the `i` direction by the *number of cells in each block* +in the `i` direction. So, with a spherical grid with one root node, and 256 +processors used, the number of blocks in the `i` and `j` direction is 256 ( = 4 +\* 4 \* 4 \* 4). Breaking it into both directions - (2\*2) \* (2*2) \* (2\*2) \* +(2\*2) or (2 \* 2 \* 2 \* 2) \* (2 \* 2 \* 2 \* 2) or (16 \* 16) - 16 and 16. +So, the total number of blocks in the `i` direction is 16 \* nLons and in the +`j` direction is 16 \* nLats. + +For the cubesphere grid, the `nX` and `nY` are the number of grid cells in the +`i` and `j` direction. At this time, these have to be identical in order to have +the grid cells match up along the boundaries between the top and bottom nodes. +The resolution of the Cubesphere grid is roughly (360$^\circ$ / (4 \* nX \* +sqrt(nProc/6))). For example, if `nX` = 18, and 24 processors are requested, +then the resolution = 360 / (4 \* 18 \* sqrt(4)) = 360 / (72 \* 2) = +2.5$^\circ$. As another example, to make a grid with 1$^\circ$ resolution with +96 processors, we get (1 = 360 / (4 \* nX \* 4) = 22.5 / nX), so `nX` has to be +around 22. (If `nX` were 22, and `nProc` = 96, then the resolution would be +1.02$^\circ$). + +### Vertical Resolution + +In all grids, the nAlts (`nZ`) are not parallelized, so the number of points +in the `k` direction is what is specified by the user. For the `sphere` and +`cubesphere` grids, this is the number of altitude points. On the `dipole` grid, +this is the number of points along the dipole flux tube. + +```json + "neuGrid" : { + "Shape" : "sphere", + "LatRange" : [-90.0, 90.0], + "nLatsPerBlock" : 18, + "LonRange" : [0.0, 360.0], + "nLonsPerBlock" : 36, + "nAlts" : 50, + "MinAlt" : 100.0, + "dAltkm" : 5.0, + "dAltScale" : 0.25, + "IsUniformAlt" : true, + "AltFile" : ""}, +``` + +```json + "ionGrid" : { + "Shape" : "dipole", + "LatRange" : [-90.0, 90.0], + "nLatsPerBlock" : 18, + "LonRange" : [0.0, 360.0], + "nLonsPerBlock" : 36, + "nAlts" : 200, + "MinAlt" : 80.0, + "MinApex" : 120.0, + "MaxAlt" : 5000.0}, +``` + +The dipole grid has both open field-lines and closed field-lines. The closed +field-lines are near the equator, while the open field-lines are near the poles. +The variable `MaxAlt` sets where this differentiation occurs - if the apex +height of the field-line is above this altitude, then it is open. All +field-lines in Aether start at the `MinAlt` and rise along a dipolar shape until +they either encounter the equatorial plane or `MaxAlt`. In the south, these +field-lines tilt towards the north (from `MinAlt` to `MaxAlt`) and in the north, +the field-lines tilt towards the south (from `MinAlt` to `MaxAlt`). + +- The spacing is uniform in longitude. +- The spacing along the field-line has non-uniform spacing. +- The spacing in latitude is non-uniform. diff --git a/doc/usage/indices.md b/doc/internals/indices.md similarity index 100% rename from doc/usage/indices.md rename to doc/internals/indices.md From 5c984fe79f2ff791934714814fdbddb18f3c73c0 Mon Sep 17 00:00:00 2001 From: "abukowski21@gmail.com" Date: Wed, 24 Jul 2024 15:36:58 -0400 Subject: [PATCH 106/127] BUG: ionGrid error prevents test success This commit adds ionGrid to the default aether.json file (in share/run/aether.json). The ionGrid specs set here hold no importance, but these three values need to be set to enable the tests to pass. Ideally, this is moved from the settings file to a function which reads the inputs (and either sets the default values or catches the error), but for now this works. --- share/run/aether.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/share/run/aether.json b/share/run/aether.json index e046c37a..a819f82e 100644 --- a/share/run/aether.json +++ b/share/run/aether.json @@ -23,7 +23,12 @@ "nLatsPerBlock" : 22, "nAlts" : 40, "dAltScale" : 0.25, - "IsUniformAlt" : false}, + "IsUniformAlt" : false}, + + "ionGrid" : { + "AltFile" : "", + "IsUniformAlt" : false, + "dAltScale" : 0.5}, "OmniwebFiles" : ["UA/inputs/omni_20110319.txt"], From 7334b946c2806bae0b2aead3d6ff48c89aacb0e7 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Tue, 3 Sep 2024 16:50:18 -0400 Subject: [PATCH 107/127] FEAT: New exponential-ish dipole grid Drafted first in python. This code makes plots & is adjustable. I commented everything so it's hopefully understandable... Reach out with questions. Similar to SAMI2/3 grid. It's B_par & B_perp aligned, made in dipole (p,q) coords, and specified w/ (nf, nz). Code hands back (r,lat) though, not (q,p) or anything like that... Code needs to be able to be modified for tilted, offset dipole so I left all the for-loops and didn't spend any time optimizing. --- edu/examples/Dipole/dipole.py | 366 +++++++++++++++++++++++++--------- 1 file changed, 277 insertions(+), 89 deletions(-) diff --git a/edu/examples/Dipole/dipole.py b/edu/examples/Dipole/dipole.py index 3d372177..f8bae674 100755 --- a/edu/examples/Dipole/dipole.py +++ b/edu/examples/Dipole/dipole.py @@ -1,105 +1,293 @@ -#!/usr/bin/env python3 - import matplotlib.pyplot as plt import numpy as np -def get_lshell(x, y, z): - # x, y, z need to be normalized to radius of planet! - - xy = np.sqrt(x * x + y * y) - xyz = np.sqrt(x * x + y * y + z * z) - cosLat = xy / xyz - lshell = 1.0 / (cosLat * cosLat) - return lshell - -def get_lshell_sphere(lat, r): - # r needs to be normalized to radius of planet! - cosLat = np.cos(lat) - lshell = r / (cosLat * cosLat) - return lshell - -def get_lat_from_r_and_lshell(r, lshell): - cosLat = np.sqrt(r / lshell) - cosLat[cosLat > 1] = 1.0 - cosLat[cosLat < -1] = -1.0 - lat = np.arccos(cosLat) - return lat - -def get_bfield(lat, r): - # r should be normalized to radius of planet - # b is normalized to Bequator at surface - oor3 = (1.0 / r)**3 - br = -2 * oor3 * np.cos(lat) - bt = -1 * oor3 * np.sin(lat) - return bt, br - -def get_uniform_spacing(lat, rmin, rmax, nPts): - lshell = get_lshell_sphere(lat, 1.0) - print('building field line for lat : ', lat) - print(' --> lshell : ', lshell) - if (lshell < rmax): - rmax = lshell - print(' --> lshell is limiter!') - else: - print(' --> rmax is limiter : ', rmax) - r = np.linspace(rmin, rmax, num = nPts) - return r - -def get_r3_spacing(lat, rmin, rmax, nPts): - lshell = get_lshell_sphere(lat, 1.0) - print('building field line for lat : ', lat) - print(' --> lshell : ', lshell) - if (lshell < rmax): - rmax = lshell - print(' --> lshell is limiter!') - else: - print(' --> rmax is limiter : ', rmax) - - r1 = rmin**(1.0/3.0) - r2 = rmax**(1.0/3.0) - r3 = np.linspace(r1, r2, num = nPts) - r = r3**3 - return r - -#------------------------------------------------------------------------ +#### Set inputs #### + +# Set to None or '' to not save, just show (pan/zoom capabilities). +fig_save_path = None + + +nf = 100 # number of field lines +nz = 200 # number of grid cells on each field line +alt_min = 90 # in km, min altitude + +# in degrees, start/end latitudes of field lines. between (90,0) +max_blat = 89.9 +min_blat = 2.25 + +gams = 0.1 +# point distribution along field lines. +# higher puts more pts at high altitudes. + +baselat_spacing_factor = 6 # exponential factor for spacing baselats (uses cos() too) + +# consts: +Re = 6371 # in km + + +# leave empty for "auto" (aaron b choose), or put in custom limits here. +# [left, right, bottom, top] +limits_left_plot = None # default is whole image +limits_right_plot = None # default is [-0.1, 2.0, -0.3, 1.75] + + +# ------------------------------------------------------------------------ # Main code is here: -#------------------------------------------------------------------------ +# ------------------------------------------------------------------------ + +#### Useful Functions: #### + + +def calc_baselats(blat_min, blat_max, n_f, factor): + """Lay down base latitudes + + Args: + blat_min (float): min latitude to start (positive), in degrees. + blat_max (float): max latitude in degrees, positiv & less than 90.0 + nf (int): Number of field lines. Must be even! + factor (float): Factor to use to space latitudes. + + Returns: + numpy array: starting latitudes + + Notes: follows very similar approach to doi:10.1029/2000JA000035 + - Not exactly exponential, but exponential and a cosine! + - Spaced according to B-field strength. + + """ + # Space out base latitudes: + + baselats = np.linspace( + np.cos(np.deg2rad(blat_min ** (1 / factor))), + np.cos(np.deg2rad(blat_max ** (1 / factor))), + num=n_f, + ) + + # baselats are in southern hemisphere + baselats = -1 * np.flip(np.rad2deg(np.arccos(baselats)) ** factor) + + return baselats + + +def calc_q(alt, theta, re=None, is_alt=False, isnt_re=False): + """Calculate q (distance along field line) for a point + + Args: + alt (float, array-like): altitude (kn or Re) + theta (float or array-like): magnetic latitude (in degrees). + 0 at mag equator (measured from North Pole). + re (float): radius of earth in km. only used if alt is altitude and/or in km + is_alt (bool): altitude is altitude? False means it's radius. Default is False + isnt_re (bool): altitude is in km? False means altitude has units [Re]. Default is False + + Returns: + (array or float): q-value for the given (alt, theta) point. + + Notes: + - make sure theta has units degrees and is measured from north pole + - See for more information. + - is_alt & isnt_re are from debugging & aren't used anymore. + + """ + + if is_alt: # convert altitude to radius + alt = alt + re + if isnt_re: # convert km to re + alt = alt / re + + return np.cos(np.deg2rad(90 - theta)) / (alt**2) + + +def calc_p(alt, theta, re=None, is_alt=False, isnt_re=False): + """Calculate p-value (l-shell) for a given altitude & latitude + + Args: + alt (float): altitude (in km or Re), (above surface or from origin) + theta (float): latitude, in degrees + re (float): earth radius in km. optional. + is_alt (bool): is alt altitude? if it's radius, set to False (default). + isnt_re (bool): altitude is in km? False (default) means altitude has units [Re]. + + Returns: + (array or float): p-value. Same as l-Shell, in Re. + + Notes: + - make sure theta has units degrees and is measured from north pole + - See for more information. + - is_alt & isnt_re are from debugging & aren't used anymore. + + """ + if is_alt: + alt = alt + re + if isnt_re: + alt = alt / re + return alt / (np.sin(np.deg2rad(90 - theta)) ** 2) + + +def qp_solve(q, p): + """Solve for radius given (q,p) dipole coordinates + Methodology from equator + qpnew = [] + for i in range(nzh): + delq = qp0[i] - q_S + qpnew.append(q_S + ft[i] * delq) + + # qpnew is from south-equator. extend it to north pole, + # so *-1 & reverse order so it is ascending + qpnew.extend(np.flip(np.array(qpnew)) * -1) + + ilats = [] + irs = [] + + for i in range(nz0): + # use qpsolve to get r from (q,p) + irs.append(qp_solve(qpnew[i], pvals[f_iter])) + # Use dipole equations to get lat from q and r + # q = cos(theta)/r**2 + ilats.append(np.rad2deg(np.arcsin(qpnew[i] * irs[-1] ** 2))) + + # Put into 2-D; lists easier & then return numpy + lats_2d.append(ilats) + rs_2d.append(irs) + + return np.array(lats_2d), np.array(rs_2d) + + +#### Actual computation: #### + + +AltMinRe = (alt_min + Re) / Re # alt min in Re + +# baselats +baselats = calc_baselats(min_blat, max_blat, nf, baselat_spacing_factor) + +# l-shells +pvals = calc_p(AltMinRe, baselats) + +# take those, make lats & radii +lats, rs = calc_exp_grid(nf, nz, AltMinRe, baselats, pvals, gams) -nPts = 20 -rEarth = 6372.0 -altMin = 100.0 -altMax = 3*rEarth -rMin = (rEarth + altMin) / rEarth -rMax = (rEarth + altMax) / rEarth +print("making plot") -fig = plt.figure(figsize = (10,10)) -ax = fig.add_axes([0.1,0.1,0.8,0.8]) +# change variables in case anyone wants to make different plots +xs = lats[:, :] +ys = rs[:, :] -baseLats = np.arange(-80, 90, 10) +fig, ax = plt.subplots(1, 2, figsize=(7, 7)) -for baseLat in baseLats: +# scatter points, same color is same field line +for x, y in zip(xs, ys): + ax[0].scatter(y * np.cos(np.deg2rad(x)), y * np.sin(np.deg2rad(x))) + ax[1].scatter(y * np.cos(np.deg2rad(x)), y * np.sin(np.deg2rad(x))) - lat = baseLat * np.pi / 180.0 - lshell = get_lshell_sphere(lat, 1.0) - print('lshell : ', lshell) - r = get_r3_spacing(lat, rMin, rMax, nPts) +# change variables again, overwrite previous xs, ys +# Take every 4th field line so it's more clear to see things +xs = lats[:, :-8:4] +ys = rs[:, :-8:4] +# black dashed lines, same nz value at different nf's +for x, y in zip(xs.T, ys.T): + ax[0].plot( + y * np.cos(np.deg2rad(x)), y * np.sin(np.deg2rad(x)), linestyle="--", color="k" + ) + ax[1].plot( + y * np.cos(np.deg2rad(x)), y * np.sin(np.deg2rad(x)), linestyle="--", color="k" + ) - lats = get_lat_from_r_and_lshell(r, lshell) - if (baseLat < 0): - lats = -lats +# make square-ish +ax[0].set_aspect(1) +ax[1].set_aspect(1) - x = r * np.cos(lats) - z = r * np.sin(lats) +# custom limits? +if limits_left_plot: + ax[1].set_xlim(limits_left_plot[0], limits_left_plot[1]) + ax[1].set_ylim(limits_left_plot[2], limits_left_plot[3]) - ax.plot(x, z) -ax.set_ylim([-rMax*1.1, rMax*1.1]) -ax.set_xlim([-rMax*1.1, rMax*1.1]) -ax.set_aspect(1.0) +if limits_right_plot: + ax[1].set_xlim(limits_right_plot[0], limits_right_plot[1]) + ax[1].set_ylim(limits_right_plot[2], limits_right_plot[3]) +else: + ax[1].set_xlim(-0.1, 2) + ax[1].set_ylim(-0.3, 1.75) -plotfile = 'test.png' -print('Writing plot : ', plotfile) -fig.savefig(plotfile) -plt.close() +# save or show: +if fig_save_path: + plt.savefig(fig_save_path) +else: + plt.show() +plt.close("all") From e1db675d55751f0b0ba8dc950da3c9d6f451339d Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 5 Sep 2024 18:28:10 -0400 Subject: [PATCH 108/127] [feat]: Use analytic solution to find (r,theta) from (q,p) when initalizing mag grid Paper on this from 2006: --- src/init_mag_grid.cpp | 55 ++++++++----------------------------------- 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 5f327dbb..58cae908 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -629,63 +629,28 @@ void Grid::fill_dipole_q_line(precision_t qN, precision_t qS, precision_t Gamma, // ---------------------------------------------------------------------- -// Routine to convert p and q to r and theta. Appraoch is to first solve -// for r using eq 4 from Huba et al 2000. q^2*r^4+1/q*r-1=0 -// This is solved numerically using Newton-Raphson (NR) technique. -// Once we know r we can reover theta from p=r*1/(sin theta)^2. -// note r here is normalized to planet radius. +// Routine to convert p and q to r and theta. Can be solved iteratively, +// or with approach from (swisdak, 2006) +// who solved it analytically: +// https://arxiv.org/pdf/physics/0606044 // // ---------------------------------------------------------------------- std::pair Grid::p_q_to_r_theta(precision_t p, precision_t q) { //return quanties precision_t r, theta; // function value and derivative for NR method - precision_t Func, dFunc; - // tolerance for root finding - precision_t Tolerance = 0.00001; - - // initial guess for r - r = 100.0; - - Func= pow(q,2.0) * pow(r,4.0) + 1.0/p*r-1; - dFunc= 4.0*pow(q,2.0) * pow(r,3.0) + 1.0/p; + precision_t term0, term1, term2, term3; - // cout<< "p,q="< Tolerance) { - try { - Func= pow(q,2.0) * pow(r,4.0) + 1.0/p*r-1; - dFunc= 4.0*pow(q,2.0) * pow(r,3.0) + 1.0/p; - - // in NR method r(i+1)=r(i)-f/f' for each iteration - - r = r - Func/dFunc; - - if (++itr > maxItr){ throw(itr);} - } - catch (int itr){ - cout<<"WARN: exceeded max #iterations.. exiting "; - exit(10); - } - // cout << r << " " << Func << " "<< dFunc << endl; - } + double term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); + double term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); + double term2 = pow(term0, 1.0 / 3.0); + double term3 = 0.5 * pow(((pow(term1,2) + term1 * term2 + pow(term2,2)) / term1), 3.0 / 2.0); + double r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); // now that r is determined we can solve for theta //theta = asin(sqrt(r/p)); theta = acos(q*pow(r,2.0)); - - //cout << "for p,q = " << p <<" "<< q << endl; - //cout << " r = " << r << endl; - //cout << " theta = " << theta << endl; - //cout << endl; - return {r,theta}; } From c0c292a570ec2a925e931d03d90b4ee25c853326 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Wed, 11 Sep 2024 17:19:25 -0400 Subject: [PATCH 109/127] [feat]: Use analytic solution to find (r,theta) from (q,p) when initalizing mag grid Paper on this from 2006: --- src/init_mag_grid.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 58cae908..308e0348 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -638,14 +638,15 @@ void Grid::fill_dipole_q_line(precision_t qN, precision_t qS, precision_t Gamma, std::pair Grid::p_q_to_r_theta(precision_t p, precision_t q) { //return quanties precision_t r, theta; - // function value and derivative for NR method + // Intermediate quantities: precision_t term0, term1, term2, term3; - double term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); - double term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); - double term2 = pow(term0, 1.0 / 3.0); - double term3 = 0.5 * pow(((pow(term1,2) + term1 * term2 + pow(term2,2)) / term1), 3.0 / 2.0); - double r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); + term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); + term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); + term2 = pow(term0, 1.0 / 3.0); + term3 = 0.5 * pow(((pow(term1,2) + term1 * term2 + pow(term2,2)) / term1), 3.0 / 2.0); + + r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); // now that r is determined we can solve for theta //theta = asin(sqrt(r/p)); From 32b068beb9f50244ea49081152e84cbba91bbea2 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Mon, 23 Sep 2024 13:20:53 -0400 Subject: [PATCH 110/127] [feat?]: Dipole in c++, not in Aether though... Working. Need to port to init_mag_grid.cpp --- edu/examples/Dipole/grid.cpp | 167 +++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 edu/examples/Dipole/grid.cpp diff --git a/edu/examples/Dipole/grid.cpp b/edu/examples/Dipole/grid.cpp new file mode 100644 index 00000000..9e018acf --- /dev/null +++ b/edu/examples/Dipole/grid.cpp @@ -0,0 +1,167 @@ + +#include +#include +#include +#include + +using arma_vec = arma::Col; +using arma_mat = arma::Mat; + +inline double deg2rad(double degrees) +{ + // function compiled inline to convert degrees to radians + static const double pi_on_180 = 4.0 * atan(1.0) / 180.0; + return degrees * pi_on_180; +} + +inline double rad2deg(double degrees) +{ + // function compiled inline to convert degrees to radians + static const double pi_on_180 = 4.0 * atan(1.0) / 180.0; + return degrees / pi_on_180; + // return degrees; +} + +double qp_solve(double q, double p) +{ + double term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); + double term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); + double term2 = pow(term0, 1.0 / 3.0); + double term3 = 0.5 * pow(((pow(term1, 2) + term1 * term2 + pow(term2, 2)) / term1), 3.0 / 2.0); + double new_r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); + return new_r; +} + +int main() +{ + int nF = 100; + int nZ = 200; + float alt_min = 90.0; + float min_blat = 2.25; + float max_blat = 89.9; + + // user-defined constants + double gams = 0.2; + double baselat_spacing = 6.0; + + // constants + int iErr = 0; + float Re = 6371.0; + float pi = 3.14159; + float pio2 = pi / 2.0; + bool didWork = true; + + int nFby2 = nF / 2; + int nZby2 = nZ / 2; + double altmin_inRe = (alt_min + Re) / Re; + + // outputs + arma_mat bLats(nF, nZ), bLons(nF, nZ), bAlts(nF, nZ); + + arma_vec baseLats(nF); + //, bLons(nF, nZ), bAlts(nF, nZ); + + // TODO: REFACTOR FROM HERE TO **1 ? + // Lay down baseLat spacing according to an exponential factor: + double del_lat, blat_min_, blat_max_, tmp_lat; + blat_min_ = cos(deg2rad(pow(min_blat, 1.0 / baselat_spacing))); + blat_max_ = cos(deg2rad(pow(max_blat, 1.0 / baselat_spacing))); + del_lat = (blat_max_ - blat_min_) / (nF - 1.0); + + for (int i = 0; i < nF; i++) + { + // first put down "linear" spacing + tmp_lat = blat_min_ + del_lat * i; + // then scale it according to the exponent & convert back to deg + tmp_lat = pow(rad2deg(acos(tmp_lat)), baselat_spacing); + // place values in array backwards, S => N hemis + baseLats(nF - i - 1) = -tmp_lat; + } + + // Find L-Shell for each baseLat + // using L=R/sin2(theta), where theta is from north pole + arma_vec Lshells(nF); + for (int i = 0; i < nF; i++) + { + Lshells(i) = ((alt_min + Re) / Re) / pow(sin(deg2rad(90.0 - baseLats(i))), 2.0); + } + + // allocate & calculate some things outside of the main loop + // fa, fb, fc are factors to make the code easier to read + double q_S, q_N, delqp, fb; + double qp0, fb0, ft, delq, qp2; + arma_vec exp_q_dist(nZ), q_vals(nZ); + + for (int i = 0; i < nZ; i++) + { + exp_q_dist(i) = gams + (1 - gams) * exp(-pow(((i - nZby2) / (nZ / 10.0)), 2.0)); + } + + for (int i_nF = 0; i_nF < nF; i_nF++) + { + + // min/max q + q_N = cos(deg2rad(90.0 + baseLats(i_nF))) / pow((alt_min + Re) / Re, 2.0); + q_S = cos(deg2rad(90 - baseLats(i_nF))) / pow((alt_min + Re) / Re, 2.0); + + // calculate const. stride similar to sami2/3 (huba & joyce 2000) + // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == + // first loop for southern hemisphere, second for north. + for (int i_nZ = 0; i_nZ < nZby2; i_nZ++) + { + delqp = (q_N - q_S) / nZ; + qp0 = q_S + i_nZ * (delqp); + delqp = altmin_inRe * delqp; + fb0 = (1 - exp_q_dist(i_nZ)) / exp(-q_S / delqp - 1); + ft = exp_q_dist(i_nZ) - fb0 + fb0 * exp(-(qp0 - q_S) / delqp); + + delq = qp0 - q_S; + qp2 = q_S + ft * delq; + + bAlts(i_nF, i_nZ) = qp_solve(qp2, Lshells(i_nF)); + bLats(i_nF, i_nZ) = rad2deg(asin(qp2 * pow(bAlts(i_nF, i_nZ), 2.0))); + + // test mirroring across hemi's + + bAlts(i_nF, nZ - i_nZ - 1) = qp_solve(-qp2, Lshells(i_nF)); + bLats(i_nF, nZ - i_nZ - 1) = -bLats(i_nF, i_nZ); + } + } + + // calculate const. stride similar to sami2/3 (huba & joyce 2000) + // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == + // first loop for southern hemisphere, second for north. + // for (int i_nZ=nZ; i_nZ>nZby2; i_nZ++){ + // delqp = (q_N - q_S)/nZ; + // qp0 = q_S + i_nZ*(delqp); + // delqp = altmin_inRe * delqp; + // fb0 = (1-exp_q_dist(i_nZ)) / exp(-q_S/delqp - 1); + // fa = exp_q_dist(i_nZ) - fb0; + // ft = fa + fb0 * exp(-(qp0 - q_S)/delqp); + // + // delq = qp0 - q_S; + // qp2 = q_S + ft * delq; + ////fb = (exp_q_dist(i_nZ) - fa) + fa * exp(-(exp_q_dist(i_nZ) - q_S) / delqp); + ////fb = (exp_q_dist(i_nZ) - fa) + fa * exp(-(q_S + i_nZ * delqp/altmin_inRe) / i_nZ); + // bAlts(i_nF, i_nZ) = qp_solve(qp2, Lshells(i_nF)); + // bLats(i_nF, i_nZ) = rad2deg(asin(qp2 * pow(bAlts(i_nF, i_nZ), 2.0))); + // } + // } + //} + //} + //} + + std::ofstream fout; + fout.open("grid.csv"); + fout << "nf,nz,lat,alt,baselat\n"; + for (int fi = 0; fi < nF; fi++) + { + for (int zi = 0; zi < nZ; zi++) + { + fout << fi << "," << zi << "," << bLats(fi, zi) << "," << bAlts(fi, zi) << "," << baseLats(fi) << "\n"; + } + } + fout.close(); + + return iErr; +} From 0a90abffd0171ca7c0a53ee82e5484bf08d77122 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 16:38:41 -0400 Subject: [PATCH 111/127] [feat]: Initial commit with dipole magnetic grid Couple issues remain: - Longitudes are sometimes nan in output files? - nLats needs to be even. And it's not checked. - Ghost cells probably not implemented correctly. --- include/grid.h | 66 ++- include/inputs.h | 4 +- src/dipole.cpp | 7 - src/init_mag_grid.cpp | 1211 +++++++++++++++++++++-------------------- src/inputs.cpp | 433 ++++++++++----- 5 files changed, 948 insertions(+), 773 deletions(-) diff --git a/include/grid.h b/include/grid.h index 60935fcb..e6b38aa4 100644 --- a/include/grid.h +++ b/include/grid.h @@ -11,10 +11,10 @@ // Grid class // ---------------------------------------------------------------------------- -class Grid { +class Grid +{ public: - const int iSphere_ = 1; const int iCubesphere_ = 2; const int iDipole_ = 3; @@ -186,7 +186,7 @@ class Grid { void calc_rad_unit(Planets planet); void calc_gravity(Planets planet); bool init_geo_grid(Quadtree quadtree, - Planets planet); + Planets planet); void create_sphere_connection(Quadtree quadtree); void create_sphere_grid(Quadtree quadtree); void create_cubesphere_connection(Quadtree quadtree); @@ -199,15 +199,21 @@ class Grid { void calc_cent_acc(Planets planet); // Make mag-field grid: - void init_mag_grid(Planets planet); - std::pair lshell_to_qn_qs(Planets planet, precision_t Lshell, precision_t Lon, precision_t AltMin); - void convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], precision_t XyzGeo[3]); - void fill_dipole_q_line(precision_t qN, precision_t qS, precision_t Gamma, int nZ, precision_t Lshell, precision_t Lon, double *q); - std::pair p_q_to_r_theta(precision_t p, precision_t q); - arma_vec get_r3_spacing(precision_t lat, precision_t rMin, - precision_t rMax, int64_t nPts, int64_t nGcs); + std::pair lshell_to_qn_qs(Planets planet, + precision_t Lshell, + precision_t Lon, + precision_t AltMin); + void convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], + precision_t XyzGeo[3]); + std::pair fill_dipole_q_line(precision_t qN_, + precision_t qS_, + precision_t Gamma_, + int64_t nZ_, + precision_t Lshell_, + precision_t min_alt_); + std::pair qp_to_r_theta(precision_t q, precision_t p); void init_dipole_grid(Quadtree quadtree, Planets planet); - + arma_vec rNorm1d, lat1dalong; // Update ghost cells with values from other processors void exchange(arma_cube &data, const bool pole_inverse); @@ -237,7 +243,8 @@ class Grid { int64_t iRootYp; int64_t iRootYm; - struct messages_struct { + struct messages_struct + { int64_t iFace; int64_t iProc_to; int64_t iSizeTotal; @@ -249,8 +256,8 @@ class Grid { /// Variables needed for asynchronous message passing MPI_Request requests; - precision_t* buffer; - precision_t* rbuffer; + precision_t *buffer; + precision_t *rbuffer; // For cubesphere. these are needed for interpolation // when the cells go onto a different face: @@ -263,13 +270,13 @@ class Grid { bool gcInterpolationSet = false; messages_struct make_new_interconnection(int64_t iDir, - int64_t nVars, - int64_t iProc_to, - arma_vec edge_center, - bool IsPole, - bool DoReverseX, - bool DoReverseY, - bool XbecomesY); + int64_t nVars, + int64_t iProc_to, + arma_vec edge_center, + bool IsPole, + bool DoReverseX, + bool DoReverseY, + bool XbecomesY); bool send_one_face(int64_t iFace); bool send_one_var_one_face(int64_t iFace); @@ -299,8 +306,7 @@ class Grid { */ std::vector get_interpolation_values(const arma_cube &data) const; - private: - +private: bool IsGeoGrid; bool HasBField; bool IsExperimental; @@ -315,7 +321,8 @@ class Grid { // interpolation members // The struct representing the range of a spherical grid - struct sphere_range { + struct sphere_range + { precision_t lon_min; precision_t lon_max; precision_t dLon; @@ -326,7 +333,8 @@ class Grid { precision_t alt_max; }; // The struct representing the range of a cubesphere grid - struct cubesphere_range { + struct cubesphere_range + { // The minimum value and delta change of row and col // We don't use row_max and col_max because they are not promised to be // greater than min, for example the right norm of suface 2 expands along @@ -357,7 +365,8 @@ class Grid { // Each point is processed by the function set_interpolation_coefs and stored // in the form of this structure. // If the point is out of the grid, in_grid = false and all other members are undefined - struct interp_coef_t { + struct interp_coef_t + { // The point is inside the cube of [iRow, iRow+1], [iCol, iCol+1], [iAlt, iAlt+1] uint64_t iRow; uint64_t iCol; @@ -394,7 +403,8 @@ class Grid { // Initialize connections between processors void init_connection(); // Used for message exchange - struct idx2d_t { + struct idx2d_t + { // Index of row and column int64_t ilon; int64_t ilat; @@ -409,4 +419,4 @@ class Grid { MPI_Comm grid_comm; }; -#endif // INCLUDE_GRID_H_ +#endif // INCLUDE_GRID_H_ diff --git a/include/inputs.h b/include/inputs.h index 7fc5d70d..03fc16c6 100644 --- a/include/inputs.h +++ b/include/inputs.h @@ -61,7 +61,9 @@ class Inputs { bool IsUniformAlt; // Only needed for Mag Field grid: - precision_t min_apex; + // min_apex (not used) and LatStretch is used + // as lat = min_lat + dlat where dlat = acos(cos(lat^stretch))^(1/stretch) + precision_t min_apex, LatStretch, FieldLineStretch, max_lat_dipole; // Some grid shapes allow specification of altitudes based on a file: std::string alt_file; diff --git a/src/dipole.cpp b/src/dipole.cpp index 26fdf278..8f1175ec 100644 --- a/src/dipole.cpp +++ b/src/dipole.cpp @@ -16,13 +16,6 @@ precision_t get_lshell(precision_t lat, precision_t rNorm) { return lshell; } -arma_vec get_lat_from_r_and_lshell(arma_vec r, precision_t lshell) { - arma_vec cosLat = sqrt(r / lshell); - cosLat.clamp(-1.0, 1.0); - arma_vec lat = acos(cosLat); - return lat; -} - precision_t get_lat_from_r_and_lshell(precision_t r, precision_t lshell) { precision_t cosLat = sqrt(r / lshell); if (cosLat < -1.0) cosLat = -1.0; diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 308e0348..5661feb2 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -7,326 +7,20 @@ #include "../include/aether.h" // ---------------------------------------------------------------------- -// Initialize the geographic grid. At the moment, this is a simple -// Lon/Lat/Alt grid. The grid structure is general enough that each -// of the lon, lat, and alt can be a function of the other variables. -// ---------------------------------------------------------------------- - -void Grid::init_mag_grid(Planets planet) { - - std::string function = "Grid::init_mag_grid"; - static int iFunction = -1; - report.enter(function, iFunction); - -// turn the switch on! - set_IsGeoGrid(false); - - // This is just an example: - Inputs::grid_input_struct grid_input = input.get_grid_inputs("ionGrid"); - - int64_t iLon, iLat, iAlt; - - // SHOW(grid_input.dalt); exit(10); - - // Longitudes: - // - Make a 1d vector - // - copy it into the 3d cube - arma_vec lon1d(nLons); - precision_t dlon = (grid_input.lon_max - grid_input.lon_min) / (nLons-2*nGCs); - - for (iLon=0; iLon < nLons; iLon++) - lon1d[iLon] = grid_input.lon_min + (iLon-nGCs+0.5) * dlon; - - for (iLat=0; iLat < nLats; iLat++) { - for (iAlt=0; iAlt < nAlts; iAlt++) { - - magLon_scgc.subcube(0, iLat, iAlt, nLons-1, iLat, iAlt) = lon1d; - - // AD: does below make sense? - // magPhi_scgc.subcube(0, iLat, iAlt, nLons-1, iLat, iAlt) = lon1d; - - } - } - - - // cout << "from"<< function<< "nLats= "<< nLats<<"\n"< - } - - // SHOW(lshell); - // SHOW(lat1d); exit(10); - - - //< - for (iLon = 0; iLon < nLons; iLon++) { - for (iAlt = 0; iAlt < nAlts; iAlt++) - this->magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; - } - //> - - - for (iLon=0; iLon < nLons; iLon++) { - for (iAlt=0; iAlt < nAlts; iAlt++) { - - //magP_scgc.subcube(iLon, 0, iAlt, iLon, nLats-1, iAlt) = lshell; - - for (iLat=0; iLatgeoLon_scgc(iX,iY,iZ) = magPhi_scgc(iX,iY,iZ); - this->geoLat_scgc(iX,iY,iZ) = theta; - this->geoAlt_scgc(iX,iY,iZ) = r; - - this->geoAlt_scgc(iX,iY,iZ) *= radius0; - - - // - grid_input.alt_min ? - - //> - - } - } - } - - - - - - // save 3D mag grid for examination - std::fstream gridfile; - gridfile.open ("grid3D.dat",ios::out); - gridfile.precision(std::numeric_limits::digits10); - - // write header - gridfile << "VARIABLES = \"X\", \"Y\", \"Z\" " << endl; - gridfile << "Zone I = "<< nZ << ",J = " << nY << ",K = "<< nX - <<", DATAPACKING=POINT" << endl; - - // write grid data - for (iX=0; iX Grid::lshell_to_qn_qs(Planets planet, precision_t Lshell, precision_t Lon, precision_t AltMin) { +std::pair Grid::lshell_to_qn_qs(Planets planet, + precision_t Lshell, + precision_t Lon, + precision_t AltMin) +{ std::string function = "Grid::lshell_to_qn_qs"; static int iFunction = -1; report.enter(function, iFunction); - precision_t qN,qS; - + precision_t qN, qS; + precision_t XyzDipoleLeft[3], XyzDipoleMid[3], XyzDipoleRight[3]; precision_t XyzGeoLeft[3], XyzGeoMid[3], XyzGeoRight[3]; precision_t rGeoLeft, rGeoMid, rGeoRight; @@ -334,176 +28,197 @@ std::pair Grid::lshell_to_qn_qs(Planets planet, precisi precision_t ThetaTilt, PhiTilt; precision_t Lat, Radius, rMin; // Named dimension constants - static int Lon_= 0, Lat_= 1, Radius_= 2; - - //bound vars for bisection search + static int Lon_ = 0, Lat_ = 1, Radius_ = 2; + + // bound vars for bisection search precision_t ThetaRight, ThetaLeft, ThetaMid; - precision_t rDipoleLeft,rDipoleMid,rDipoleRight; - - //Stopping condition for bisection search + precision_t rDipoleLeft, rDipoleMid, rDipoleRight; + + // Stopping condition for bisection search precision_t DeltaTheta; precision_t Tolerance = 1e-4; - + // status vars for bisection search int iStatusLeft, iStatusRight, iStatusMid; // note we normalize Lshell by equatorial radius precision_t RadiusEq = planet.get_radius(0.0); - // loop for qN and qS - for(int iQ = 0; iQ < 2; iQ++){ + for (int iQ = 0; iQ < 2; iQ++) + { - if (iQ == 0){ + if (iQ == 0) + { // set initial left, mid, right bounds for bisection search for qN - ThetaRight = 0.5*cPI; - ThetaLeft = 1.0*cDtoR; - ThetaMid = 0.5*(ThetaRight+ThetaLeft); - }else{ + ThetaRight = 0.5 * cPI; + ThetaLeft = 1.0 * cDtoR; + ThetaMid = 0.5 * (ThetaRight + ThetaLeft); + } + else + { // set initial left, mid, right bounds for bisection search for qS - ThetaLeft = 0.5*cPI; - ThetaRight = 179.0*cDtoR; - ThetaMid = 0.5*(ThetaRight+ThetaLeft); + ThetaLeft = 0.5 * cPI; + ThetaRight = 179.0 * cDtoR; + ThetaMid = 0.5 * (ThetaRight + ThetaLeft); } - + // Initial stopping condition stopping condition - DeltaTheta = abs(ThetaLeft-ThetaRight); - - + DeltaTheta = abs(ThetaLeft - ThetaRight); + // start bisection search for qN - while( DeltaTheta > Tolerance ) { - + while (DeltaTheta > Tolerance) + { + // find rDipole that cooresponds to these Left,Mid,Right - // ThetaDipole values - rDipoleLeft = Lshell * pow(sin(ThetaLeft),2.0); - rDipoleMid = Lshell * pow(sin(ThetaMid),2.0); - rDipoleRight = Lshell * pow(sin(ThetaRight),2.0); - + // ThetaDipole values + rDipoleLeft = Lshell * pow(sin(ThetaLeft), 2.0); + rDipoleMid = Lshell * pow(sin(ThetaMid), 2.0); + rDipoleRight = Lshell * pow(sin(ThetaRight), 2.0); + // Compute XyzDipole for left, mid,right states LlrDipoleLeft[Lon_] = Lon; - LlrDipoleLeft[Lat_] = 0.5*cPI-ThetaLeft; + LlrDipoleLeft[Lat_] = 0.5 * cPI - ThetaLeft; LlrDipoleLeft[Radius_] = rDipoleLeft; transform_llr_to_xyz(LlrDipoleLeft, XyzDipoleLeft); - + LlrDipoleMid[Lon_] = Lon; - LlrDipoleMid[Lat_] = 0.5*cPI-ThetaMid; + LlrDipoleMid[Lat_] = 0.5 * cPI - ThetaMid; LlrDipoleMid[Radius_] = rDipoleMid; transform_llr_to_xyz(LlrDipoleMid, XyzDipoleMid); - + LlrDipoleRight[Lon_] = Lon; - LlrDipoleRight[Lat_] = 0.5*cPI-ThetaRight; + LlrDipoleRight[Lat_] = 0.5 * cPI - ThetaRight; LlrDipoleRight[Radius_] = rDipoleRight; transform_llr_to_xyz(LlrDipoleRight, XyzDipoleRight); - + // Transform to XyzGeo and unnormalize convert_dipole_geo_xyz(planet, XyzDipoleLeft, XyzGeoLeft); convert_dipole_geo_xyz(planet, XyzDipoleMid, XyzGeoMid); convert_dipole_geo_xyz(planet, XyzDipoleRight, XyzGeoRight); - - //cout << "XyzGeoLeft[0]" << XyzGeoLeft[0] << endl; - //cout << "XyzGeoLeft[1]" << XyzGeoLeft[1] << endl; - //cout << "XyzGeoLeft[2]" << XyzGeoLeft[2] << endl; - - XyzGeoLeft[0]=XyzGeoLeft[0]*RadiusEq; - XyzGeoLeft[1]=XyzGeoLeft[1]*RadiusEq; - XyzGeoLeft[2]=XyzGeoLeft[2]*RadiusEq; - - abort; - - XyzGeoMid[0]=XyzGeoMid[0]*RadiusEq; - XyzGeoMid[1]=XyzGeoMid[1]*RadiusEq; - XyzGeoMid[2]=XyzGeoMid[2]*RadiusEq; - - XyzGeoRight[0]=XyzGeoRight[0]*RadiusEq; - XyzGeoRight[1]=XyzGeoRight[1]*RadiusEq; - XyzGeoRight[2]=XyzGeoRight[2]*RadiusEq; - + + // cout << "XyzGeoLeft[0]" << XyzGeoLeft[0] << endl; + // cout << "XyzGeoLeft[1]" << XyzGeoLeft[1] << endl; + // cout << "XyzGeoLeft[2]" << XyzGeoLeft[2] << endl; + + XyzGeoLeft[0] = XyzGeoLeft[0] * RadiusEq; + XyzGeoLeft[1] = XyzGeoLeft[1] * RadiusEq; + XyzGeoLeft[2] = XyzGeoLeft[2] * RadiusEq; + + // abort; + + XyzGeoMid[0] = XyzGeoMid[0] * RadiusEq; + XyzGeoMid[1] = XyzGeoMid[1] * RadiusEq; + XyzGeoMid[2] = XyzGeoMid[2] * RadiusEq; + + XyzGeoRight[0] = XyzGeoRight[0] * RadiusEq; + XyzGeoRight[1] = XyzGeoRight[1] * RadiusEq; + XyzGeoRight[2] = XyzGeoRight[2] * RadiusEq; + // Compute radius in geo coordinate for comparison to rmin - rGeoLeft = - sqrt(pow(XyzGeoLeft[0],2)+pow(XyzGeoLeft[1],2)+pow(XyzGeoLeft[2],2)); - rGeoMid = - sqrt(pow(XyzGeoMid[0],2)+pow(XyzGeoMid[1],2)+pow(XyzGeoMid[2],2)); - rGeoRight = - sqrt(pow(XyzGeoRight[0],2)+pow(XyzGeoRight[1],2)+pow(XyzGeoRight[2],2)); - + rGeoLeft = sqrt(pow(XyzGeoLeft[0], 2) + pow(XyzGeoLeft[1], 2) + pow(XyzGeoLeft[2], 2)); + rGeoMid = sqrt(pow(XyzGeoMid[0], 2) + pow(XyzGeoMid[1], 2) + pow(XyzGeoMid[2], 2)); + rGeoRight = sqrt(pow(XyzGeoRight[0], 2) + pow(XyzGeoRight[1], 2) + pow(XyzGeoRight[2], 2)); + // get rmin for given latitude. Radius is lat dependent in general. // also find status in (0) or out (1) of rMin - Lat = 0.5*cPI-acos(XyzGeoLeft[2]/rGeoLeft); + Lat = 0.5 * cPI - acos(XyzGeoLeft[2] / rGeoLeft); Radius = planet.get_radius(Lat); - rMin = Radius+AltMin; - if (rGeoLeft < rMin){ - iStatusLeft = 0; - }else{ - iStatusLeft = 1; + rMin = Radius + AltMin; + if (rGeoLeft < rMin) + { + iStatusLeft = 0; } - - Lat = 0.5*cPI-acos(XyzGeoMid[2]/rGeoMid); + else + { + iStatusLeft = 1; + } + + Lat = 0.5 * cPI - acos(XyzGeoMid[2] / rGeoMid); Radius = planet.get_radius(Lat); - rMin = Radius+AltMin; - if (rGeoMid < rMin){ - iStatusMid = 0; - }else{ - iStatusMid = 1; + rMin = Radius + AltMin; + if (rGeoMid < rMin) + { + iStatusMid = 0; } - - Lat = 0.5*cPI-acos(XyzGeoRight[2]/rGeoRight); + else + { + iStatusMid = 1; + } + + Lat = 0.5 * cPI - acos(XyzGeoRight[2] / rGeoRight); Radius = planet.get_radius(Lat); - rMin = Radius+AltMin; - if (rGeoRight < rMin){ - iStatusRight = 0; - }else{ - iStatusRight = 1; + rMin = Radius + AltMin; + if (rGeoRight < rMin) + { + iStatusRight = 0; + } + else + { + iStatusRight = 1; } - + // Use status values to update left, right and mid values of theta - if (iStatusMid == 0) { - if (iStatusRight == 1){ - // Mid becomes left and right stays right - ThetaLeft = ThetaMid; - ThetaMid = 0.5*(ThetaLeft+ThetaRight); - }else{ - // Mid becomes right and left stays left - ThetaRight = ThetaMid; - ThetaMid = 0.5*(ThetaLeft+ThetaRight); - } - }else{ - if (iStatusRight == 0){ - // Mid becomes left and right stays right - ThetaLeft = ThetaMid; - ThetaMid = 0.5*(ThetaLeft+ThetaRight); - }else{ - // Mid becomes right and left stays left - ThetaRight = ThetaMid; - ThetaMid = 0.5*(ThetaLeft+ThetaRight); - } + if (iStatusMid == 0) + { + if (iStatusRight == 1) + { + // Mid becomes left and right stays right + ThetaLeft = ThetaMid; + ThetaMid = 0.5 * (ThetaLeft + ThetaRight); + } + else + { + // Mid becomes right and left stays left + ThetaRight = ThetaMid; + ThetaMid = 0.5 * (ThetaLeft + ThetaRight); + } + } + else + { + if (iStatusRight == 0) + { + // Mid becomes left and right stays right + ThetaLeft = ThetaMid; + ThetaMid = 0.5 * (ThetaLeft + ThetaRight); + } + else + { + // Mid becomes right and left stays left + ThetaRight = ThetaMid; + ThetaMid = 0.5 * (ThetaLeft + ThetaRight); + } } // Update stopping condition - DeltaTheta = abs(ThetaLeft-ThetaRight); + DeltaTheta = abs(ThetaLeft - ThetaRight); } - - //set the q value - rDipoleMid = Lshell * pow(sin(ThetaMid),2.0); - if (iQ ==0){ - qN = pow(rDipoleMid,-2.0)*cos(ThetaMid); + + // set the q value + rDipoleMid = Lshell * pow(sin(ThetaMid), 2.0); + if (iQ == 0) + { + qN = pow(rDipoleMid, -2.0) * cos(ThetaMid); // cout << "!!! For L = " << Lshell << endl; // cout << "!!! qN = " << qN << endl; // cout << "!!! ThetaMid = " << ThetaMid*cRtoD << endl; - }else{ - qS = pow(rDipoleMid,-2.0)*cos(ThetaMid); + } + else + { + qS = pow(rDipoleMid, -2.0) * cos(ThetaMid); // cout << "!!! qS = " << qS << endl; } } report.exit(function); - return {qN,qS}; - + return {qN, qS}; } // ----------------------------------------------------------------------- // Convert XyzDipole to XyzGeo -// +// // ----------------------------------------------------------------------- -void Grid::convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], precision_t XyzGeo[3]) { +void Grid::convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], precision_t XyzGeo[3]) +{ precision_t XyzRemoveShift[3]; precision_t XyzRemoveTilt[3]; precision_t XyzRemoveRot[3]; @@ -513,15 +228,14 @@ void Grid::convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], prec precision_t magnetic_pole_rotation = planet.get_dipole_rotation(); precision_t radius = planet.get_radius(0.0); - - // get the dipole shift, but normalize it to equatorial radius + // get the dipole shift, but normalize it to equatorial radius precision_t dipole_center[3]; std::vector temp_dipole_center = planet.get_dipole_center(); transform_float_vector_to_array(temp_dipole_center, dipole_center); - dipole_center[0]=dipole_center[0]/radius; - dipole_center[1]=dipole_center[1]/radius; - dipole_center[2]=dipole_center[2]/radius; + dipole_center[0] = dipole_center[0] / radius; + dipole_center[1] = dipole_center[1] / radius; + dipole_center[2] = dipole_center[2] / radius; // Remove Tilt transform_rot_y(XyzDipole, magnetic_pole_tilt, XyzRemoveTilt); @@ -532,159 +246,360 @@ void Grid::convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], prec // Remove Shift vector_add(XyzRemoveRot, dipole_center, XyzGeo); -// cout << "XyzDipole[0]" << XyzDipole[0] << endl; -// cout << "XyzDipole[1]" << XyzDipole[1] << endl; -// cout << "XyzDipole[2]" << XyzDipole[2] << endl; -// -// cout << "XyzGeo[0]" << XyzGeo[0] << endl; -// cout << "XyzGeo[1]" << XyzGeo[1] << endl; -// cout << "XyzGeo[2]" << XyzGeo[2] << endl; - + // cout << "XyzDipole[0]" << XyzDipole[0] << endl; + // cout << "XyzDipole[1]" << XyzDipole[1] << endl; + // cout << "XyzDipole[2]" << XyzDipole[2] << endl; + // + // cout << "XyzGeo[0]" << XyzGeo[0] << endl; + // cout << "XyzGeo[1]" << XyzGeo[1] << endl; + // cout << "XyzGeo[2]" << XyzGeo[2] << endl; } // ---------------------------------------------------------------------- // Routine to fill in the q values for a particular L and lon // using equations 7-8 from Huba et al 2000 // ---------------------------------------------------------------------- -void Grid::fill_dipole_q_line(precision_t qN, precision_t qS, precision_t Gamma, int nZ, precision_t Lshell, precision_t Lon, double *q) { +std::pair Grid::fill_dipole_q_line(precision_t qN_, + precision_t qS_, + precision_t Gamma_, + int64_t nZ_, + precision_t lShell_, + precision_t min_alt_) + +{ + std::string function = "Grid::fill_dipole_q_line"; static int iFunction = -1; report.enter(function, iFunction); - - double x[nZ]; - double r[nZ]; - double theta[nZ]; - double Dx; - precision_t Llr[3], Xyz[3]; - int DoTestLine = 0; - - //open test file for writing the grid data for plotting - std::fstream gridfile; - if (DoTestLine==1){ - gridfile.open ("grid.dat",ios::out); - } - - // set the Dx (c in equation 8 from Huba et al 2000) - // Note this equation has a typo in it. The proper - // version would be found by defining the bounds of - // x from equation 7, and then dividing that into - // equal segments. - //Dx = 2.0*(1.0-sinh(Gamma*qN))/((static_cast(nZ)-1.0)*sinh(Gamma*qS)); - - Dx = (sinh(Gamma*qS)-sinh(Gamma*qN))/((static_cast(nZ)-1.0)*sinh(Gamma*qS)); - - //Dx = 2.0/(static_cast(nZ)-1.0); - //Dx = (static_cast(qN)-static_cast(qS))/(static_cast(nZ)-1.0); - - //cout << "Dx = " << Dx << endl; - //cout << "Gamma = " << Gamma << endl; - //cout << "nZ = " << nZ << endl; - //cout << "qN = " << qN << endl; - //cout << "qS = " << qS << endl; - - // set initial x[0] value using eq. 7 from Huba et al with qi=qN - x[0] = sinh(Gamma*qN)/sinh(Gamma*qS); - q[0] = asinh(x[0]*sinh(Gamma*qS))/Gamma; - - // fill x_i=x_(i-1)+Dx - for(int i = 1; i < nZ; i++){ - x[i] = x[i-1]+Dx; - q[i] = asinh(x[i]*sinh(Gamma*qS))/Gamma; - - // For given q and p we can now find cooresponding r and theta in - // dipole coord. Starty by numerically solving for r (normalized) using - // equation 4 of Huba et al 2000 given by q^2r^4+1/p r -1 = 0 - auto rtheta = p_q_to_r_theta(Lshell, q[i]); - - r[i] = rtheta.first; - theta[i] = rtheta.second; - - //cout << "i, q " << i << " " << q[i] << endl; - //cout << "i, x " << i << " " << x[i] << endl; - //cout << "i, r, theta " << i << " " << r[i]<<" "< Grid::p_q_to_r_theta(precision_t p, precision_t q) { - //return quanties +std::pair Grid::qp_to_r_theta(precision_t q, precision_t p) +{ + // return quanties precision_t r, theta; // Intermediate quantities: precision_t term0, term1, term2, term3; - + term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); term2 = pow(term0, 1.0 / 3.0); - term3 = 0.5 * pow(((pow(term1,2) + term1 * term2 + pow(term2,2)) / term1), 3.0 / 2.0); - + term3 = 0.5 * pow(((pow(term1, 2) + term1 * term2 + pow(term2, 2)) / term1), 3.0 / 2.0); + r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); - + // now that r is determined we can solve for theta - //theta = asin(sqrt(r/p)); - theta = acos(q*pow(r,2.0)); - - return {r,theta}; -} + // theta = asin(sqrt(r/p)); + theta = asin(q * pow(r, 2.0)); -arma_vec Grid::get_r3_spacing(precision_t lat, precision_t rMin, - precision_t rMax, int64_t nPts, int64_t nGcs) { - - precision_t rMaxReal = rMax; - - precision_t lShell = get_lshell(lat, rMin); - if (lShell < rMaxReal) - rMaxReal = lShell; - precision_t rMin3 = pow(rMin, 1.0/3.0); - precision_t rMax3 = pow(rMaxReal, 1.0/3.0); - precision_t dr3 = (rMax3 - rMin3) / (nPts-nGcs*2); - arma_vec r(nPts); - for (int64_t iPt = 0; iPt < nPts; iPt++) - r(iPt) = pow(rMin3 + dr3 * (iPt - nGcs), 3); - return r; + return {r, theta}; } // ---------------------------------------------------------------------- -// Initialize the geographic grid. At the moment, this is a simple +// Initialize the dipole grid. At the moment, Aaron B needs to update this string. +// #TODO: FIX THIS! // Lon/Lat/Alt grid. The grid structure is general enough that each // of the lon, lat, and alt can be a function of the other variables. // ---------------------------------------------------------------------- -void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) { - +// void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) +// { + +// std::string function = "Grid::init_dipole_grid"; +// static int iFunction = -1; +// report.enter(function, iFunction); + +// // turn the switch on! +// IsGeoGrid = false; +// IsMagGrid = true; + +// int64_t iLon, iLat, iAlt; + +// report.print(0, "Creating Dipole Grid"); + +// report.print(3, "Getting mgrid_inputs inputs in dipole grid"); + +// Inputs::grid_input_struct grid_input = input.get_grid_inputs("ionGrid"); + +// // Get inputs +// report.print(3, "Setting inputs in dipole grid"); +// // Convert altitudes from km to m: +// precision_t min_apex = grid_input.min_apex * cKMtoM; +// precision_t min_alt = grid_input.alt_min * cKMtoM; +// precision_t LatStretch = grid_input.LatStretch; +// precision_t Gamma = grid_input.FieldLineStretch; +// precision_t max_lat = grid_input.max_lat_dipole; + +// // "Sanitize" the inputs +// precision_t planetRadius = planet.get_radius(0.0); +// precision_t min_lshell = (min_apex + planetRadius) / planetRadius; +// precision_t min_alt_re = (min_alt + planetRadius) / planetRadius; +// // precision_t max_r = (max_alt + planetRadius) / planetRadius; + +// // Get some coordinates and sizes in normalized coordinates: +// arma_vec lower_left_norm = quadtree.get_vect("LL"); +// arma_vec size_right_norm = quadtree.get_vect("SR"); +// arma_vec size_up_norm = quadtree.get_vect("SU"); + +// // ALB needs these for new grid +// precision_t qN, qS, Lon; + +// // LONGITUDES: +// precision_t dlon = size_right_norm(0) * cPI / (nLons - 2 * nGCs); +// precision_t lon0 = lower_left_norm(0) * cPI; +// arma_vec lon1d(nLons); +// // - Make a 1d vector +// // - copy it into the 3d cube +// for (iLon = 0; iLon < nLons; iLon++) +// { +// lon1d(iLon) = lon0 + (iLon - nGCs + 0.5) * dlon; +// } + +// for (iLat = 0; iLat < nLats; iLat++) +// { +// for (iAlt = 0; iAlt < nAlts; iAlt++) +// magLon_scgc.subcube(0, iLat, iAlt, nLons - 1, iLat, iAlt) = lon1d; +// } + +// geoLon_scgc = magLon_scgc; + +// // Base-latitudes: +// // some stretching is recommended (required 6??). +// // - Make a 1d vector +// // - copy it into the 3d cube + +// // precision_t dlat = size_up_norm(1) * cPI / (nLats - 2 * nGCs); +// // !QUESTION: Use dlat here or (max-min)/total ?? + +// precision_t lat0 = lower_left_norm(1) * cPI; +// arma_vec lat1d(nLats); + +// precision_t min_lat = get_lat_from_r_and_lshell(1.0, min_lshell); +// // max_lat defined earlier in inputs + +// // lay down spacing that's linear in cos^(1/latStretch) +// precision_t min_lat_, max_lat_; +// min_lat_ = cos(pow(min_lat, 1 / LatStretch)); +// max_lat_ = cos(pow(max_lat, 1 / LatStretch)); +// // see !QUESTION above +// precision_t dlat = (max_lat_ - min_lat_) / (nLats - 2 * nGCs); + +// for (iLat = nLats / 2; iLat < nLats; iLat++) +// { +// lat1d(iLat) = pow(acos(min_lat_ + (iLat - nGCs + 0.5) * dlat), +// LatStretch); +// // THIS WILL NOT WORK FOR OFFSET DIPOLES: +// lat1d(nLats - iLat - 1) = -lat1d(iLat); +// } + +// for (iLon = 0; iLon < nLons; iLon++) +// { +// for (iAlt = 0; iAlt < nAlts; iAlt++) +// { +// magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; +// } +// } + +// arma_vec rNorm1d(nAlts), lat1dAlong(nAlts); +// arma_cube r3d(nLons, nLats, nAlts); +// precision_t lShell; + +// rad_unit_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); + +// for (iLat = 0; iLat < nLats / 2; iLat++) +// { +// lat0 = lat1d(iLat); +// lat0 = cPI - lat0; + +// lShell = get_lshell(lat0, min_alt_re); + +// // with lShell, go thru and compute q (along field line) points at each longitude +// for (iLon = 0; iLon < nLons; iLon++) +// { +// Lon = magPhi_scgc(iLon, iLat, 1); + +// // This may not be working as expected: +// // auto Qvals = lshell_to_qn_qs(planet, lShell, Lon, min_alt); +// // qN = Qvals.first; +// // qS = Qvals.second; + +// qN = cos((cPI / 2 + lat0)) / pow(min_alt_re, 2.0); +// qS = cos((cPI / 2 - lat0)) / pow(min_alt_re, 2.0); +// // std:: cout<< qN<<" \t"< cPI / 2) +// // lat1dAlong(iAlt) = R_LAT_qline.first(iAlt); +// // if (lat1d(iLat) < -cPI / 2) +// lat1dAlong(iAlt) = R_LAT_qline.first(iAlt); +// rNorm1d(iAlt) = R_LAT_qline.second(iAlt); +// } +// // r3d.tube(iLon, iLat) = rNorm1d; +// r3d.tube(iLon, iLat) = rNorm1d - planetRadius; +// magLat_scgc.tube(iLon, iLat) = lat1dAlong; +// } +// } +// // return; +// // std::ofstream fout0; +// // fout0.open("grid0.csv"); +// // fout0 << "nl,nf,nz,lon,lat,alt,baselat\n"; +// // for (int ilarn = 0; ilarn < nLons; ilarn++) +// // { +// // for (int ilart = 0; ilart < nLats; ilart++) +// // { +// // for (int zi = 0; zi < nZ; zi++) +// // { +// // fout0 << ilarn << "," << ilart << "," << zi << "," << magLon_scgc(ilarn, ilart, zi) +// // << "," << magLat_scgc(ilarn, ilart, zi) << "," << r3d(ilarn, ilart, zi) << "\n"; +// // } +// // } +// // fout0.close(); + +// // return ; + +// geoLat_scgc = magLat_scgc; +// magAlt_scgc = r3d - planetRadius; +// geoAlt_scgc = magAlt_scgc; + +// // Calculate the radius, etc: +// fill_grid_radius(planet); + +// // Figure out what direction is radial: +// rad_unit_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); +// gravity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); + +// for (int iV = 0; iV < 3; iV++) +// { +// rad_unit_vcgc[iV].zeros(); +// gravity_vcgc[iV].zeros(); +// } + +// arma_cube br = 2 * sin(abs(magLat_scgc)); +// arma_cube bt = cos(magLat_scgc); +// arma_cube bm = sqrt(br % br + bt % bt); +// // Latitudinal direction of radial: +// arma_cube s = sign(magLat_scgc); +// s.elem(find(s == 0)).ones(); + +// rad_unit_vcgc[1] = bt / bm % s; +// rad_unit_vcgc[2] = -br / bm; + +// precision_t mu = planet.get_mu(); +// gravity_vcgc[1] = mu * rad_unit_vcgc[1] % radius2i_scgc; +// gravity_vcgc[2] = mu * rad_unit_vcgc[2] % radius2i_scgc; +// gravity_potential_scgc.set_size(nX, nY, nAlts); +// gravity_potential_scgc.zeros(); +// gravity_mag_scgc = sqrt( +// gravity_vcgc[0] % gravity_vcgc[0] + +// gravity_vcgc[1] % gravity_vcgc[1] + +// gravity_vcgc[2] % gravity_vcgc[2]); + +// std::vector llr, xyz, xyzRot1, xyzRot2; +// llr.push_back(magLon_scgc); +// llr.push_back(magLat_scgc); +// llr.push_back(r3d); +// xyz = transform_llr_to_xyz_3d(llr); + +// precision_t magnetic_pole_rotation = planet.get_dipole_rotation(); +// precision_t magnetic_pole_tilt = planet.get_dipole_tilt(); + +// // Reverse our dipole rotations: +// xyzRot1 = rotate_around_y_3d(xyz, magnetic_pole_tilt); +// xyzRot2 = rotate_around_z_3d(xyzRot1, magnetic_pole_rotation); + +// // transform back to lon, lat, radius: +// llr = transform_xyz_to_llr_3d(xyzRot2); + +// geoLon_scgc = llr[0]; +// geoLat_scgc = llr[1]; +// geoAlt_scgc = llr[2] - planetRadius; + +// std::ofstream fout; +// fout.open("grid.csv"); +// fout << "nl,nf,nz,lon,lat,alt,baselat\n"; +// for (int ilarn = 0; ilarn < nLons; ilarn++) +// { +// for (int ilart = 0; ilart < nLats; ilart++) +// { +// for (int zi = 0; zi < nZ; zi++) +// { +// fout << ilarn << "," << ilart << "," << zi << "," << geoLon_scgc(ilarn, ilart, zi) +// << "," << geoLat_scgc(ilarn, ilart, zi) << "," << geoAlt_scgc(ilarn, ilart, zi) << "\n"; +// } +// } +// fout.close(); + +// // return ; +// } + +// // for (iLon = 0; iLon < nLons; iLon++) +// // { +// // for (iAlt = 0; iAlt < nAlts; iAlt++) +// // { +// // magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; +// // } +// // } + +// calc_alt_grid_spacing(); + +// // Calculate magnetic field and magnetic coordinates: +// fill_grid_bfield(planet); + +// report.exit(function); +// return; +// } + +void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) +{ + std::string function = "Grid::init_dipole_grid"; static int iFunction = -1; report.enter(function, iFunction); - -// turn the switch on! + + // turn the switch on! IsGeoGrid = false; IsMagGrid = true; @@ -696,82 +611,195 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) { Inputs::grid_input_struct grid_input = input.get_grid_inputs("ionGrid"); + // Get inputs report.print(3, "Setting inputs in dipole grid"); // Convert altitudes from km to m: precision_t min_apex = grid_input.min_apex * cKMtoM; precision_t min_alt = grid_input.alt_min * cKMtoM; - precision_t max_alt = grid_input.alt_max * cKMtoM; + precision_t LatStretch = grid_input.LatStretch; + precision_t Gamma = grid_input.FieldLineStretch; + precision_t max_lat = grid_input.max_lat_dipole; + + // "Sanitize" the inputs precision_t planetRadius = planet.get_radius(0.0); - precision_t min_lshell = (min_apex + planetRadius)/planetRadius; - precision_t min_r = (min_alt + planetRadius)/planetRadius; - precision_t max_r = (max_alt + planetRadius)/planetRadius; - precision_t min_lat = get_lat_from_r_and_lshell(min_r, min_lshell); - precision_t stretch = (cPI/2 - min_lat) / (cPI/2); - report.print(3, "Done setting inputs in dipole grid"); + precision_t min_lshell = (min_apex + planetRadius) / planetRadius; + precision_t min_alt_re = (min_alt + planetRadius) / planetRadius; + // precision_t max_r = (max_alt + planetRadius) / planetRadius; // Get some coordinates and sizes in normalized coordinates: arma_vec lower_left_norm = quadtree.get_vect("LL"); arma_vec size_right_norm = quadtree.get_vect("SR"); arma_vec size_up_norm = quadtree.get_vect("SU"); + // ALB needs these for new grid + precision_t qN, qS, Lon; + + // LONGITUDES: precision_t dlon = size_right_norm(0) * cPI / (nLons - 2 * nGCs); precision_t lon0 = lower_left_norm(0) * cPI; arma_vec lon1d(nLons); - - // Longitudes: // - Make a 1d vector // - copy it into the 3d cube for (iLon = 0; iLon < nLons; iLon++) + { lon1d(iLon) = lon0 + (iLon - nGCs + 0.5) * dlon; + } - for (iLat = 0; iLat < nLats; iLat++) { + for (iLat = 0; iLat < nLats; iLat++) + { for (iAlt = 0; iAlt < nAlts; iAlt++) magLon_scgc.subcube(0, iLat, iAlt, nLons - 1, iLat, iAlt) = lon1d; } geoLon_scgc = magLon_scgc; - precision_t dlat = size_up_norm(1) * cPI / (nLats - 2 * nGCs); + // Base-latitudes: + // some stretching is recommended (required 6??). + // - Make a 1d vector + // - copy it into the 3d cube + + // precision_t dlat = size_up_norm(1) * cPI / (nLats - 2 * nGCs); + // !QUESTION: Use dlat here or (max-min)/total ?? + precision_t lat0 = lower_left_norm(1) * cPI; arma_vec lat1d(nLats); - // Latitudes: - // - Make a 1d vector - // - copy it into the 3d cube - for (iLat = 0; iLat < nLats; iLat++) { - lat1d(iLat) = lat0 + (iLat - nGCs + 0.5) * dlat; - if (lat1d(iLat) >= 0) { - lat1d(iLat) = min_lat + lat1d(iLat) * stretch; - } else { - lat1d(iLat) = -min_lat + lat1d(iLat) * stretch; - } + precision_t min_lat = get_lat_from_r_and_lshell(1.0, min_lshell); + + ////////////////////////// + + // Lay down baseLat spacing according to an exponential factor: + precision_t del_lat, blat_min_, blat_max_, tmp_lat; + // This is all going to be done on full field lines. split into halves later + int64_t nF = nLats/2, nZ = nAlts, nZby2 = nAlts / 2; + arma_vec Lshells(nF), baseLats(nF); + + blat_min_ = cos(pow(min_lat, 1.0 / LatStretch)); + blat_max_ = cos(pow(max_lat, 1.0 / LatStretch)); + del_lat = (blat_max_ - blat_min_) / (nF - nGCs*2.0); + + for (int i = 0; i < nF; i++) + { + // first put down "linear" spacing + tmp_lat = blat_min_ + del_lat * (i-nGCs+0.5); + // then scale it according to the exponent & convert back to deg + tmp_lat = pow(acos(tmp_lat), LatStretch); + // place values in array backwards, S => N hemis + baseLats(nF - i - 1) = -tmp_lat; + } + + // Find L-Shell for each baseLat + // using L=R/sin2(theta), where theta is from north pole + for (int i = 0; i < nF; i++) + { + Lshells(i) = (min_alt_re) / pow(sin(cPI / 2 - baseLats(i)), 2.0); + } + + ////////////////// + + // for (iLon = 0; iLon < nLons; iLon++) + // { + // for (iAlt = 0; iAlt < nAlts; iAlt++) + // { + // magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; + // } + // } + + /////////// + + // allocate & calculate some things outside of the main loop + // fa, fb, fc are factors to make the code easier to read + precision_t q_S, q_N, delqp, fa, fb; + precision_t qp0, fb0, ft, delq, qp2; + arma_vec exp_q_dist(nZ), q_vals(nZ); + arma_mat bAlts(nF, nZ), bLats(nF, nZ); + + precision_t term0, term1, term2, term3, new_r; + + for (int i = 0; i < nZ; i++) + { + exp_q_dist(i) = Gamma + (1 - Gamma) * exp(-pow(((i - nZby2) / (nZ / 10.0)), 2.0)); + } + + for (int i_nF = 0; i_nF < nF; i_nF++) + { + + // min/max q + q_S = - cos(cPI / 2 + baseLats(i_nF)) / pow(min_alt_re, 2.0); + q_N = -q_S;//cos(cPI / 2 + baseLats(i_nF)) / pow(min_alt_re, 2.0); + + // calculate const. stride similar to sami2/3 (huba & joyce 2000) + // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == + // first loop for southern hemisphere, second for north. + for (int i_nZ = 0; i_nZ < nZ; i_nZ++) + { + // std::cout< cPI/2) lat0 = cPI - lat0; - if (lat0 < -cPI/2) lat0 = -cPI - lat0; - lShell = get_lshell(lat0, min_r); - rNorm1d = get_r3_spacing(lat0, min_r, max_r, nAlts, nGCs); - lat1dAlong = get_lat_from_r_and_lshell(rNorm1d, lShell); - - if (lat0 < 0) - lat1dAlong = -1.0 * lat1dAlong; - for (iLon = 0; iLon < nLons; iLon++) { + // std::ofstream fout0; + // fout0.open("grid0.csv"); + // fout0 << "nl,nf,nz,lon,lat,alt,baselat\n"; + + for (iLat = 0; iLat < nLats/2; iLat++) + { + for (iLon = 0; iLon < nLons; iLon++) + { + Lon = magPhi_scgc(iLon, iLat, 1); + + for (iAlt = 0; iAlt < nAlts; iAlt++) + { + lat1dAlong(iAlt) = bLats(iLat, iAlt); + rNorm1d(iAlt) = bAlts(iLat, iAlt); + } r3d.tube(iLon, iLat) = rNorm1d * planetRadius; + r3d.tube(iLon, nLats-iLat-1) = rNorm1d * planetRadius; magLat_scgc.tube(iLon, iLat) = lat1dAlong; + magLat_scgc.tube(iLon, nLats-iLat-1) = -lat1dAlong; } } + geoLat_scgc = magLat_scgc; magAlt_scgc = r3d - planetRadius; geoAlt_scgc = magAlt_scgc; @@ -783,30 +811,31 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) { rad_unit_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); gravity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); - for (int iV = 0; iV < 3; iV++) { + for (int iV = 0; iV < 3; iV++) + { rad_unit_vcgc[iV].zeros(); gravity_vcgc[iV].zeros(); } arma_cube br = 2 * sin(abs(magLat_scgc)); - arma_cube bt = cos(magLat_scgc); + arma_cube bt = cos(magLat_scgc); arma_cube bm = sqrt(br % br + bt % bt); // Latitudinal direction of radial: arma_cube s = sign(magLat_scgc); - s.elem( find(s == 0) ).ones(); + s.elem(find(s == 0)).ones(); rad_unit_vcgc[1] = bt / bm % s; - rad_unit_vcgc[2] = - br / bm; + rad_unit_vcgc[2] = -br / bm; precision_t mu = planet.get_mu(); gravity_vcgc[1] = mu * rad_unit_vcgc[1] % radius2i_scgc; gravity_vcgc[2] = mu * rad_unit_vcgc[2] % radius2i_scgc; - gravity_potential_scgc.set_size(nX, nY, nZ); + gravity_potential_scgc.set_size(nX, nY, nAlts); gravity_potential_scgc.zeros(); gravity_mag_scgc = sqrt( - gravity_vcgc[0] % gravity_vcgc[0] + - gravity_vcgc[1] % gravity_vcgc[1] + - gravity_vcgc[2] % gravity_vcgc[2]); + gravity_vcgc[0] % gravity_vcgc[0] + + gravity_vcgc[1] % gravity_vcgc[1] + + gravity_vcgc[2] % gravity_vcgc[2]); std::vector llr, xyz, xyzRot1, xyzRot2; llr.push_back(magLon_scgc); @@ -835,6 +864,4 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) { report.exit(function); return; - } - diff --git a/src/inputs.cpp b/src/inputs.cpp index 3b7f69ac..15ea40d8 100644 --- a/src/inputs.cpp +++ b/src/inputs.cpp @@ -15,7 +15,8 @@ Inputs input; // The setting of initial values should probably be moved. // ----------------------------------------------------------------------- -Inputs::Inputs(Times &time) { +Inputs::Inputs(Times &time) +{ // ------------------------------------------------ // Set some defaults: @@ -36,18 +37,24 @@ Inputs::Inputs(Times &time) { nLatsGeo = 20; nAltsGeo = 40; - if (nLonsGeo == 1) { + if (nLonsGeo == 1) + { geo_grid_input.lon_min = 0.0; geo_grid_input.lon_max = 0.0; - } else { + } + else + { geo_grid_input.lon_min = 0.0; geo_grid_input.lon_max = 2.0 * cPI; } - if (nLatsGeo == 1) { + if (nLatsGeo == 1) + { geo_grid_input.lat_min = 0.0; geo_grid_input.lat_max = 0.0; - } else { + } + else + { geo_grid_input.lat_min = -cPI / 2; geo_grid_input.lat_max = cPI / 2; } @@ -64,7 +71,8 @@ Inputs::Inputs(Times &time) { // Now read the input file: isOk = read_inputs_json(time); - if (report.test_verbose(1)) { + if (report.test_verbose(1)) + { std::cout << "Settings read in:\n"; std::cout << std::setw(2) << settings; } @@ -77,10 +85,12 @@ Inputs::Inputs(Times &time) { // output the settings json to a file (for restart) // ----------------------------------------------------------------------- -bool Inputs::write_restart() { +bool Inputs::write_restart() +{ bool didWork = true; - if (iProc == 0) { + if (iProc == 0) + { std::string filename = get_setting_str("Restart", "OutDir"); filename = filename + "/settings.json"; didWork = write_json(filename, settings); @@ -90,14 +100,13 @@ bool Inputs::write_restart() { return didWork; } - // ----------------------------------------------------------------------- // General check functions to see if keys exist: // check settings and throw invalid_argument error // if the setting doesn't exist // ----------------------------------------------------------------------- -//dummy values, to use if the settings are not set +// dummy values, to use if the settings are not set int dummy_int = -1; float dummy_float = -1; std::string dummy_string = "unknown"; @@ -106,21 +115,25 @@ std::string dummy_string = "unknown"; // 2 keys: bool Inputs::check_settings(std::string key1, - std::string key2) { + std::string key2) +{ if (report.test_verbose(2)) std::cout << "checking setting : " << key1 << " and " << key2 << "\n"; - //try to find the keys first - if (settings.find(key1) != settings.end()) { + // try to find the keys first + if (settings.find(key1) != settings.end()) + { if (settings.at(key1).find(key2) != settings.at(key1).end()) isOk = true; - } else - //if we haven't found the keys print a message & set IsOk to false + } + else + // if we haven't found the keys print a message & set IsOk to false isOk = false; - if (!isOk) { + if (!isOk) + { report.error("Error in setting : " + key1 + " : " + key2); std::cout << "Missing setting called! [" << key1 << ", " << key2 << "]\n"; } @@ -131,7 +144,8 @@ bool Inputs::check_settings(std::string key1, // ----------------------------------------------------------------------- // 1 key: -bool Inputs::check_settings(std::string key1) { +bool Inputs::check_settings(std::string key1) +{ if (report.test_verbose(2)) std::cout << "checking setting : " << key1 << "\n"; @@ -139,11 +153,12 @@ bool Inputs::check_settings(std::string key1) { if (settings.find(key1) != settings.end()) isOk = true; else - //if we haven't found the key print a message & set IsOk to false + // if we haven't found the key print a message & set IsOk to false isOk = false; - //perturb is non-essential, otherwise print error message - if (!isOk && key1 != "Perturb") { + // perturb is non-essential, otherwise print error message + if (!isOk && key1 != "Perturb") + { report.error("Error in setting : " + key1); std::cout << "Missing setting called! [" << key1 << "]\n"; } @@ -158,32 +173,38 @@ bool Inputs::check_settings(std::string key1) { // ----------------------------------------------------------------------- // a general int vector -std::vector Inputs::get_setting_intarr(std::string key1) { +std::vector Inputs::get_setting_intarr(std::string key1) +{ std::vector value; - if (check_settings(key1)) { + if (check_settings(key1)) + { int nPts = settings.at(key1).size(); isOk = true; for (int i = 0; i < nPts; i++) value.push_back(settings.at(key1).at(i)); - } else + } + else isOk = false; return value; } std::vector Inputs::get_setting_intarr(std::string key1, - std::string key2) { + std::string key2) +{ std::vector value; - if (check_settings(key1, key2)) { + if (check_settings(key1, key2)) + { int nPts = settings.at(key1).at(key2).size(); isOk = true; for (int i = 0; i < nPts; i++) value.push_back(settings.at(key1).at(key2).at(i)); - } else + } + else isOk = false; return value; @@ -192,12 +213,14 @@ std::vector Inputs::get_setting_intarr(std::string key1, // ----------------------------------------------------------------------- // A specific length int vector -std::vector Inputs::get_setting_timearr(std::string key1) { +std::vector Inputs::get_setting_timearr(std::string key1) +{ int nPtsTime = 7; std::vector outarr(nPtsTime, 0); std::vector timearr = get_setting_intarr(key1); - if (isOk) { + if (isOk) + { int nPts = timearr.size(); if (nPts > nPtsTime) @@ -213,7 +236,8 @@ std::vector Inputs::get_setting_timearr(std::string key1) { // ----------------------------------------------------------------------- // a string with 1 key: -std::string Inputs::get_setting_str(std::string key1) { +std::string Inputs::get_setting_str(std::string key1) +{ std::string value = "unknown"; if (check_settings(key1)) @@ -226,7 +250,8 @@ std::string Inputs::get_setting_str(std::string key1) { // a string with 2 keys: std::string Inputs::get_setting_str(std::string key1, - std::string key2) { + std::string key2) +{ std::string value = "unknown"; if (check_settings(key1, key2)) @@ -240,14 +265,16 @@ std::string Inputs::get_setting_str(std::string key1, std::string Inputs::get_setting_str(std::string key1, std::string key2, - std::string key3) { + std::string key3) +{ std::string value = "unknown"; isOk = false; if (settings.find(key1) != settings.end()) if (settings.at(key1).find(key2) != settings.at(key1).end()) if (settings.at(key1).at(key2).find(key3) != - settings.at(key1).at(key2).end()) { + settings.at(key1).at(key2).end()) + { value = settings.at(key1).at(key2).at(key3); isOk = true; } @@ -261,7 +288,8 @@ std::string Inputs::get_setting_str(std::string key1, // ----------------------------------------------------------------------- // an int with 1 key: -int64_t Inputs::get_setting_int(std::string key1) { +int64_t Inputs::get_setting_int(std::string key1) +{ int64_t value = LONG_MIN; if (check_settings(key1)) @@ -274,7 +302,8 @@ int64_t Inputs::get_setting_int(std::string key1) { // an int with 2 keys: int64_t Inputs::get_setting_int(std::string key1, - std::string key2) { + std::string key2) +{ int64_t value = LONG_MIN; if (check_settings(key1, key2)) @@ -286,7 +315,8 @@ int64_t Inputs::get_setting_int(std::string key1, // ----------------------------------------------------------------------- // a bool with 1 key: -bool Inputs::get_setting_bool(std::string key1) { +bool Inputs::get_setting_bool(std::string key1) +{ bool value = false; if (check_settings(key1)) @@ -299,7 +329,8 @@ bool Inputs::get_setting_bool(std::string key1) { // a bool with 2 keys: bool Inputs::get_setting_bool(std::string key1, - std::string key2) { + std::string key2) +{ bool value = false; if (check_settings(key1, key2)) @@ -313,14 +344,16 @@ bool Inputs::get_setting_bool(std::string key1, bool Inputs::get_setting_bool(std::string key1, std::string key2, - std::string key3) { + std::string key3) +{ bool value = false; isOk = false; if (settings.find(key1) != settings.end()) if (settings.at(key1).find(key2) != settings.at(key1).end()) if (settings.at(key1).at(key2).find(key3) != - settings.at(key1).at(key2).end()) { + settings.at(key1).at(key2).end()) + { value = settings.at(key1).at(key2).at(key3); isOk = true; } @@ -334,7 +367,8 @@ bool Inputs::get_setting_bool(std::string key1, // ----------------------------------------------------------------------- // a float with 1 key: -precision_t Inputs::get_setting_float(std::string key1) { +precision_t Inputs::get_setting_float(std::string key1) +{ precision_t value = std::numeric_limits::lowest(); if (check_settings(key1)) @@ -347,7 +381,8 @@ precision_t Inputs::get_setting_float(std::string key1) { // a float with 2 key: precision_t Inputs::get_setting_float(std::string key1, - std::string key2) { + std::string key2) +{ precision_t value = std::numeric_limits::lowest(); if (check_settings(key1, key2)) @@ -359,12 +394,14 @@ precision_t Inputs::get_setting_float(std::string key1, // ----------------------------------------------------------------------- // a json with 1 key: -json Inputs::get_setting_json(std::string key1) { +json Inputs::get_setting_json(std::string key1) +{ json value; if (settings.find(key1) != settings.end()) value = settings.at(key1); - else { + else + { isOk = false; report.error("Error in setting : " + key1); } @@ -376,16 +413,20 @@ json Inputs::get_setting_json(std::string key1) { // a json with 2 keys: json Inputs::get_setting_json(std::string key1, - std::string key2) { + std::string key2) +{ json value; if (settings.find(key1) != settings.end()) if (settings.at(key1).find(key2) != settings.at(key1).end()) value = settings.at(key1).at(key2); - else { + else + { isOk = false; report.error("Error in setting : " + key1 + " : " + key2); - } else { + } + else + { isOk = false; report.error("Error in setting : " + key1); } @@ -397,7 +438,8 @@ json Inputs::get_setting_json(std::string key1, // a string with 2 keys: std::string Inputs::check_settings_str(std::string key1, - std::string key2) { + std::string key2) +{ if (check_settings(key1, key2)) return settings.at(key1).at(key2); @@ -407,8 +449,10 @@ std::string Inputs::check_settings_str(std::string key1, // ----------------------------------------------------------------------- // a string with 1 key: -std::string Inputs::check_settings_str(std::string key) { - if (get_setting_str(key) == dummy_string) { +std::string Inputs::check_settings_str(std::string key) +{ + if (get_setting_str(key) == dummy_string) + { isOk = false; return dummy_string; } @@ -420,7 +464,8 @@ std::string Inputs::check_settings_str(std::string key) { // a float with 2 keys: precision_t Inputs::check_settings_pt(std::string key1, - std::string key2) { + std::string key2) +{ if (check_settings(key1, key2)) return settings.at(key1).at(key2); @@ -433,30 +478,43 @@ precision_t Inputs::check_settings_pt(std::string key1, // gridtype needs to be "neuGrid" or "ionGrid" // ----------------------------------------------------------------------- -Inputs::grid_input_struct Inputs::get_grid_inputs(std::string gridtype) { +Inputs::grid_input_struct Inputs::get_grid_inputs(std::string gridtype) +{ Inputs::grid_input_struct grid_specs; - grid_specs.shape = check_settings_str(gridtype, "Shape"); + std::vector min_max; + grid_specs.shape = check_settings_str(gridtype, "Shape"); grid_specs.nX = get_setting_int(gridtype, "nLonsPerBlock"); grid_specs.nY = get_setting_int(gridtype, "nLatsPerBlock"); grid_specs.nZ = get_setting_int(gridtype, "nAlts"); - grid_specs.alt_min = check_settings_pt(gridtype, "MinAlt"); - std::vector min_max; - min_max = get_setting_intarr(gridtype, "LatRange"); - grid_specs.lat_min = min_max[0] * cDtoR; - grid_specs.lat_max = min_max[1] * cDtoR; min_max = get_setting_intarr(gridtype, "LonRange"); grid_specs.lon_min = min_max[0] * cDtoR; grid_specs.lon_max = min_max[1] * cDtoR; - // These are only needed if the gridtype if magnetic: - if (grid_specs.shape == "dipole") { - grid_specs.alt_max = check_settings_pt(gridtype, "MaxAlt"); + grid_specs.alt_min = check_settings_pt(gridtype, "MinAlt"); + + // The rest of the settings are different for mag/geo grids, + // First take the magnetic options, then "else" should be (cube-)sphere + if (grid_specs.shape == "dipole") + { + // min_apex MUST be more than min_alt: grid_specs.min_apex = check_settings_pt(gridtype, "MinApex"); - } else { + if (grid_specs.min_apex <= grid_specs.alt_min) + { + report.error("Error in Inputs! min_apex must be more than min_alt!"); + } + grid_specs.LatStretch = check_settings_pt(gridtype, "LatStretch"); + grid_specs.max_lat_dipole = check_settings_pt(gridtype, "LatMax") * cDtoR; + grid_specs.FieldLineStretch = check_settings_pt(gridtype, "LineSpacing"); + } + else + { + min_max = get_setting_intarr(gridtype, "LatRange"); + grid_specs.lat_min = min_max[0] * cDtoR; + grid_specs.lat_max = min_max[1] * cDtoR; grid_specs.alt_file = check_settings_str(gridtype, "AltFile"); grid_specs.IsUniformAlt = get_setting_bool(gridtype, "IsUniformAlt"); if (grid_specs.IsUniformAlt) @@ -477,25 +535,32 @@ Inputs::grid_input_struct Inputs::get_grid_inputs(std::string gridtype) { // This is needed, because we may want to check for verbose specifically // in a given json and not the normal settings json: -bool Inputs::set_verbose(json in) { +bool Inputs::set_verbose(json in) +{ bool didWork = true; int iVerbose = -1; // Want to set verbose level ASAP: - if (in.contains("Debug")) { - if (in.at("Debug").contains("iVerbose")) { + if (in.contains("Debug")) + { + if (in.at("Debug").contains("iVerbose")) + { iVerbose = in.at("Debug").at("iVerbose"); - if (in.at("Debug").contains("iProc")) { + if (in.at("Debug").contains("iProc")) + { if (iProc != in.at("Debug").at("iProc")) iVerbose = -1; } - } else + } + else didWork = false; - } else + } + else didWork = false; - if (iVerbose > 0) { + if (iVerbose > 0) + { std::cout << "Setting iVerbose : " << iVerbose << "\n"; report.set_verbose(iVerbose); } @@ -507,7 +572,8 @@ bool Inputs::set_verbose(json in) { // Return total number of OMNIWeb files to read // ----------------------------------------------------------------------- -int Inputs::get_number_of_omniweb_files() { +int Inputs::get_number_of_omniweb_files() +{ if (settings.find("OmniwebFiles") != settings.end()) return settings.at("OmniwebFiles").size(); @@ -519,7 +585,8 @@ int Inputs::get_number_of_omniweb_files() { // Return OMNIWeb file names as a vector // ----------------------------------------------------------------------- -std::vector Inputs::get_omniweb_files() { +std::vector Inputs::get_omniweb_files() +{ std::vector omniweb_files; int nFiles = get_number_of_omniweb_files(); @@ -533,7 +600,8 @@ std::vector Inputs::get_omniweb_files() { // Return how often to output a given output type // ----------------------------------------------------------------------- -precision_t Inputs::get_dt_output(int iOutput) { +precision_t Inputs::get_dt_output(int iOutput) +{ precision_t value = 0.0; int nOutputs = settings.at("Outputs").at("type").size(); @@ -547,7 +615,8 @@ precision_t Inputs::get_dt_output(int iOutput) { // Return the output type // ----------------------------------------------------------------------- -std::string Inputs::get_type_output(int iOutput) { +std::string Inputs::get_type_output(int iOutput) +{ std::string value = "unknown"; int nOutputs = settings.at("Outputs").at("type").size(); @@ -561,7 +630,8 @@ std::string Inputs::get_type_output(int iOutput) { // Set random number seed // ----------------------------------------------------------------------- -void Inputs::set_seed(int seed) { +void Inputs::set_seed(int seed) +{ settings["Seed"] = seed; updated_seed = seed; } @@ -570,7 +640,8 @@ void Inputs::set_seed(int seed) { // Return random number seed that has been updated // ----------------------------------------------------------------------- -int Inputs::get_updated_seed() { +int Inputs::get_updated_seed() +{ std::default_random_engine get_random(updated_seed); updated_seed = get_random(); return updated_seed; @@ -580,7 +651,8 @@ int Inputs::get_updated_seed() { // Return log file name // ----------------------------------------------------------------------- -std::string Inputs::get_logfile() { +std::string Inputs::get_logfile() +{ std::string logfile = get_setting_str("Logfile", "name"); if (nMembers > 1) @@ -593,18 +665,26 @@ std::string Inputs::get_logfile() { // Return log file name // ----------------------------------------------------------------------- -std::string Inputs::get_logfile(int64_t iLog) { +std::string Inputs::get_logfile(int64_t iLog) +{ std::string logfile = "log.txt"; - if (check_settings("Logfile", "name")) { + if (check_settings("Logfile", "name")) + { int64_t nLogs = settings.at("Logfile").at("name").size(); - if (nLogs == 1) { + if (nLogs == 1) + { logfile = settings.at("Logfile").at("name").at(iLog); - //logfile = get_setting_str("Logfile", "name"); - } else { - if (iLog > nLogs-1) { + // logfile = get_setting_str("Logfile", "name"); + } + else + { + if (iLog > nLogs - 1) + { report.error("Error in getting logfile name!"); - logfile = settings.at("Logfile").at("name").at(nLogs-1); - } else { + logfile = settings.at("Logfile").at("name").at(nLogs - 1); + } + else + { logfile = settings.at("Logfile").at("name").at(iLog); } } @@ -619,7 +699,8 @@ std::string Inputs::get_logfile(int64_t iLog) { // Return the name of specified variables as a vector // ----------------------------------------------------------------------- -std::vector Inputs::get_species_vector() { +std::vector Inputs::get_species_vector() +{ std::vector species; const json &json_species = get_setting_json("Logfile", "species"); @@ -633,7 +714,8 @@ std::vector Inputs::get_species_vector() { // Return the name of satellite files as a vector // ----------------------------------------------------------------------- -std::vector Inputs::get_satellite_files() { +std::vector Inputs::get_satellite_files() +{ std::vector files; const json &json_files = get_setting_json("Satellites", "files"); @@ -647,7 +729,8 @@ std::vector Inputs::get_satellite_files() { // Return the output file names of satellites as a vector // ----------------------------------------------------------------------- -std::vector Inputs::get_satellite_names() { +std::vector Inputs::get_satellite_names() +{ std::vector names; const json &json_names = get_setting_json("Satellites", "names"); @@ -661,7 +744,8 @@ std::vector Inputs::get_satellite_names() { // Return how oftern to write log file for satellites as a vector // ----------------------------------------------------------------------- -std::vector Inputs::get_satellite_dts() { +std::vector Inputs::get_satellite_dts() +{ std::vector dts; const json &json_dts = get_setting_json("Satellites", "dts"); @@ -681,7 +765,8 @@ std::vector Inputs::get_satellite_dts() { // Return how oftern to write log file // ----------------------------------------------------------------------- -precision_t Inputs::get_logfile_dt() { +precision_t Inputs::get_logfile_dt() +{ return get_setting_float("Logfile", "dt"); } @@ -689,7 +774,8 @@ precision_t Inputs::get_logfile_dt() { // Return whether to append or rewrite // ----------------------------------------------------------------------- -bool Inputs::get_logfile_append() { +bool Inputs::get_logfile_append() +{ return get_setting_bool("Logfile", "append"); } @@ -697,7 +783,8 @@ bool Inputs::get_logfile_append() { // Return whether user is student // ----------------------------------------------------------------------- -bool Inputs::get_is_student() { +bool Inputs::get_is_student() +{ return get_setting_bool("Student", "is"); } @@ -705,7 +792,8 @@ bool Inputs::get_is_student() { // Return student name // ----------------------------------------------------------------------- -std::string Inputs::get_student_name() { +std::string Inputs::get_student_name() +{ return check_settings_str("Student", "name"); } @@ -713,7 +801,8 @@ std::string Inputs::get_student_name() { // Return whether grid is cubesphere or spherical // ----------------------------------------------------------------------- -bool Inputs::get_is_cubesphere() { +bool Inputs::get_is_cubesphere() +{ return get_setting_bool("CubeSphere", "is"); } @@ -721,7 +810,8 @@ bool Inputs::get_is_cubesphere() { // Return whether to restart or not // ----------------------------------------------------------------------- -bool Inputs::get_do_restart() { +bool Inputs::get_do_restart() +{ return get_setting_bool("Restart", "do"); } @@ -729,7 +819,8 @@ bool Inputs::get_do_restart() { // Return NO cooling // ----------------------------------------------------------------------- -bool Inputs::get_NO_cooling() { +bool Inputs::get_NO_cooling() +{ return get_setting_bool("Sources", "Neutrals", "NO_cool"); } @@ -737,7 +828,8 @@ bool Inputs::get_NO_cooling() { // Return O cooling // ----------------------------------------------------------------------- -bool Inputs::get_O_cooling() { +bool Inputs::get_O_cooling() +{ return get_setting_bool("Sources", "Neutrals", "O_cool"); } @@ -745,7 +837,8 @@ bool Inputs::get_O_cooling() { // Return centripetal acceleration // ----------------------------------------------------------------------- -bool Inputs::get_cent_acc() { +bool Inputs::get_cent_acc() +{ return get_setting_bool("Sources", "Grid", "Cent_acc"); } @@ -753,7 +846,8 @@ bool Inputs::get_cent_acc() { // Return restart OUT directory // ----------------------------------------------------------------------- -std::string Inputs::get_restartout_dir() { +std::string Inputs::get_restartout_dir() +{ return check_settings_str("Restart", "OutDir"); } @@ -761,7 +855,8 @@ std::string Inputs::get_restartout_dir() { // Return restart In directory // ----------------------------------------------------------------------- -std::string Inputs::get_restartin_dir() { +std::string Inputs::get_restartin_dir() +{ return check_settings_str("Restart", "InDir"); } @@ -769,7 +864,8 @@ std::string Inputs::get_restartin_dir() { // dt for writing restart files // ----------------------------------------------------------------------- -precision_t Inputs::get_dt_write_restarts() { +precision_t Inputs::get_dt_write_restarts() +{ return check_settings_pt("Restart", "dt"); } @@ -777,7 +873,8 @@ precision_t Inputs::get_dt_write_restarts() { // Return magnetic field type (dipole and none defined now.) // ----------------------------------------------------------------------- -std::string Inputs::get_bfield_type() { +std::string Inputs::get_bfield_type() +{ return check_settings_str("BField"); } @@ -785,7 +882,8 @@ std::string Inputs::get_bfield_type() { // Return whether to use EUV at all // ----------------------------------------------------------------------- -bool Inputs::get_euv_douse() { +bool Inputs::get_euv_douse() +{ return get_setting_bool("Euv", "doUse"); } @@ -794,7 +892,8 @@ bool Inputs::get_euv_douse() { // files that are for the empirical models reside // ----------------------------------------------------------------------- -std::string Inputs::get_electrodynamics_north_file() { +std::string Inputs::get_electrodynamics_north_file() +{ return check_settings_str("Electrodynamics", "NorthFile"); } @@ -803,7 +902,8 @@ std::string Inputs::get_electrodynamics_north_file() { // files that are for the empirical models reside // ----------------------------------------------------------------------- -std::string Inputs::get_electrodynamics_south_file() { +std::string Inputs::get_electrodynamics_south_file() +{ return check_settings_str("Electrodynamics", "SouthFile"); } @@ -812,7 +912,8 @@ std::string Inputs::get_electrodynamics_south_file() { // files that are for the empirical models reside // ----------------------------------------------------------------------- -std::string Inputs::get_electrodynamics_file() { +std::string Inputs::get_electrodynamics_file() +{ return check_settings_str("Electrodynamics", "File"); } @@ -821,7 +922,8 @@ std::string Inputs::get_electrodynamics_file() { // files that are for the empirical models reside // ----------------------------------------------------------------------- -std::string Inputs::get_electrodynamics_dir() { +std::string Inputs::get_electrodynamics_dir() +{ return check_settings_str("Electrodynamics", "Dir"); } @@ -829,7 +931,8 @@ std::string Inputs::get_electrodynamics_dir() { // Return the Electrodynamics Potential Model // ----------------------------------------------------------------------- -std::string Inputs::get_potential_model() { +std::string Inputs::get_potential_model() +{ return mklower(check_settings_str("Electrodynamics", "Potential")); } @@ -837,7 +940,8 @@ std::string Inputs::get_potential_model() { // Return the Electrodynamics Diffuse Auroral Model // ----------------------------------------------------------------------- -std::string Inputs::get_diffuse_auroral_model() { +std::string Inputs::get_diffuse_auroral_model() +{ return mklower(check_settings_str("Electrodynamics", "DiffuseAurora")); } @@ -845,7 +949,8 @@ std::string Inputs::get_diffuse_auroral_model() { // Return the EUV model used (EUVAC only option now) // ----------------------------------------------------------------------- -std::string Inputs::get_euv_model() { +std::string Inputs::get_euv_model() +{ return mklower(check_settings_str("Euv", "Model")); } @@ -853,7 +958,8 @@ std::string Inputs::get_euv_model() { // Return the heating efficiency of the neutrals for EUV // ----------------------------------------------------------------------- -precision_t Inputs::get_euv_heating_eff_neutrals() { +precision_t Inputs::get_euv_heating_eff_neutrals() +{ return check_settings_pt("Euv", "HeatingEfficiency"); } @@ -861,7 +967,8 @@ precision_t Inputs::get_euv_heating_eff_neutrals() { // Return whether to include the photoelectron ionization // ----------------------------------------------------------------------- -bool Inputs::get_include_photoelectrons() { +bool Inputs::get_include_photoelectrons() +{ return get_setting_bool("Euv", "IncludePhotoElectrons"); } @@ -869,7 +976,8 @@ bool Inputs::get_include_photoelectrons() { // Return how often to calculate EUV energy deposition // ----------------------------------------------------------------------- -precision_t Inputs::get_dt_euv() { +precision_t Inputs::get_dt_euv() +{ return check_settings_pt("Euv", "dt"); } @@ -877,7 +985,8 @@ precision_t Inputs::get_dt_euv() { // Return how often to report progress of simulation // ----------------------------------------------------------------------- -precision_t Inputs::get_dt_report() { +precision_t Inputs::get_dt_report() +{ return check_settings_pt("Debug", "dt"); } @@ -885,7 +994,8 @@ precision_t Inputs::get_dt_report() { // Return number of output types // ----------------------------------------------------------------------- -precision_t Inputs::get_n_outputs() { +precision_t Inputs::get_n_outputs() +{ return settings.at("Outputs").at("type").size(); } @@ -893,7 +1003,8 @@ precision_t Inputs::get_n_outputs() { // Return original random number seed // ----------------------------------------------------------------------- -int Inputs::get_original_seed() { +int Inputs::get_original_seed() +{ return get_setting_int("Seed"); } @@ -901,19 +1012,23 @@ int Inputs::get_original_seed() { // Return number of longitudes, latitudes, and altitudes in grid // ----------------------------------------------------------------------- -int Inputs::get_nLons(std::string gridtype) { +int Inputs::get_nLons(std::string gridtype) +{ return get_setting_int(gridtype, "nLonsPerBlock"); } -int Inputs::get_nLats(std::string gridtype) { +int Inputs::get_nLats(std::string gridtype) +{ return get_setting_int(gridtype, "nLatsPerBlock"); } -int Inputs::get_nAlts(std::string gridtype) { +int Inputs::get_nAlts(std::string gridtype) +{ return get_setting_int(gridtype, "nAlts"); } -std::string Inputs::get_grid_shape(std::string gridtype) { +std::string Inputs::get_grid_shape(std::string gridtype) +{ return mklower(get_setting_str(gridtype, "Shape")); } @@ -921,7 +1036,8 @@ std::string Inputs::get_grid_shape(std::string gridtype) { // Return number of ensemble members // ----------------------------------------------------------------------- -int Inputs::get_nMembers() { +int Inputs::get_nMembers() +{ return check_settings_pt("Ensembles", "nMembers"); } @@ -929,11 +1045,13 @@ int Inputs::get_nMembers() { // Return verbose variables // ----------------------------------------------------------------------- -int Inputs::get_verbose() { +int Inputs::get_verbose() +{ return check_settings_pt("Debug", "iVerbose"); } -int Inputs::get_verbose_proc() { +int Inputs::get_verbose_proc() +{ return get_setting_int("Debug", "iProc"); } @@ -941,7 +1059,8 @@ int Inputs::get_verbose_proc() { // Return EUV file name // ----------------------------------------------------------------------- -std::string Inputs::get_euv_file() { +std::string Inputs::get_euv_file() +{ return check_settings_str("Euv", "File"); } @@ -949,7 +1068,8 @@ std::string Inputs::get_euv_file() { // Return aurora file name // ----------------------------------------------------------------------- -std::string Inputs::get_aurora_file() { +std::string Inputs::get_aurora_file() +{ return check_settings_str("AuroraFile"); } @@ -957,7 +1077,8 @@ std::string Inputs::get_aurora_file() { // Return Chemistry file name // ----------------------------------------------------------------------- -std::string Inputs::get_chemistry_file() { +std::string Inputs::get_chemistry_file() +{ return settings.at("ChemistryFile"); } @@ -965,7 +1086,8 @@ std::string Inputs::get_chemistry_file() { // Return Collision file name // ----------------------------------------------------------------------- -std::string Inputs::get_collision_file() { +std::string Inputs::get_collision_file() +{ return check_settings_str("CollisionsFile"); } @@ -973,7 +1095,8 @@ std::string Inputs::get_collision_file() { // Return Indices Lookup Filename // ----------------------------------------------------------------------- -std::string Inputs::get_indices_lookup_file() { +std::string Inputs::get_indices_lookup_file() +{ return check_settings_str("IndicesLookupFile"); } @@ -981,7 +1104,8 @@ std::string Inputs::get_indices_lookup_file() { // Return F107 file to read // ----------------------------------------------------------------------- -std::string Inputs::get_f107_file() { +std::string Inputs::get_f107_file() +{ return check_settings_str("F107File"); } @@ -989,7 +1113,8 @@ std::string Inputs::get_f107_file() { // Return planet name // ----------------------------------------------------------------------- -std::string Inputs::get_planet() { +std::string Inputs::get_planet() +{ return get_setting_str("Planet", "name"); } @@ -997,7 +1122,8 @@ std::string Inputs::get_planet() { // Return file that contains (all) planetary characteristics // ----------------------------------------------------------------------- -std::string Inputs::get_planetary_file() { +std::string Inputs::get_planetary_file() +{ return check_settings_str("PlanetCharacteristicsFile"); } @@ -1006,7 +1132,8 @@ std::string Inputs::get_planetary_file() { // a given planet // ----------------------------------------------------------------------- -std::string Inputs::get_planet_species_file() { +std::string Inputs::get_planet_species_file() +{ return check_settings_str("PlanetSpeciesFile"); } @@ -1015,7 +1142,8 @@ std::string Inputs::get_planet_species_file() { // of individual ion specie temperature calculations // ----------------------------------------------------------------------- -bool Inputs::get_do_calc_bulk_ion_temp() { +bool Inputs::get_do_calc_bulk_ion_temp() +{ return get_setting_bool("DoCalcBulkIonTemp"); } @@ -1023,7 +1151,8 @@ bool Inputs::get_do_calc_bulk_ion_temp() { // Return Eddy Coefficient // ----------------------------------------------------------------------- -precision_t Inputs::get_eddy_coef() { +precision_t Inputs::get_eddy_coef() +{ return get_setting_float("Eddy", "Coefficient"); } @@ -1031,7 +1160,8 @@ precision_t Inputs::get_eddy_coef() { // Return pressure where Eddy Diffusion starts to drop off // ----------------------------------------------------------------------- -precision_t Inputs::get_eddy_bottom() { +precision_t Inputs::get_eddy_bottom() +{ return get_setting_float("Eddy", "BottomPressure"); } @@ -1039,7 +1169,8 @@ precision_t Inputs::get_eddy_bottom() { // Return pressure where Eddy Diffusion becomes zero // ----------------------------------------------------------------------- -precision_t Inputs::get_eddy_top() { +precision_t Inputs::get_eddy_top() +{ return get_setting_float("Eddy", "TopPressure"); } @@ -1047,7 +1178,8 @@ precision_t Inputs::get_eddy_top() { // // ----------------------------------------------------------------------- -bool Inputs::get_use_eddy_momentum() { +bool Inputs::get_use_eddy_momentum() +{ return get_setting_bool("Eddy", "UseInMomentum"); } @@ -1055,7 +1187,8 @@ bool Inputs::get_use_eddy_momentum() { // // ----------------------------------------------------------------------- -bool Inputs::get_use_eddy_energy() { +bool Inputs::get_use_eddy_energy() +{ return get_setting_bool("Eddy", "UseInEnergy"); } @@ -1063,7 +1196,8 @@ bool Inputs::get_use_eddy_energy() { // // ----------------------------------------------------------------------- -json Inputs::get_perturb_values() { +json Inputs::get_perturb_values() +{ return get_setting_json("Perturb"); } @@ -1071,7 +1205,8 @@ json Inputs::get_perturb_values() { // Flag to check neutral and ions for nans and infinites // ----------------------------------------------------------------------- -bool Inputs::get_check_for_nans() { +bool Inputs::get_check_for_nans() +{ return get_setting_bool("Debug", "check_for_nans"); } @@ -1079,7 +1214,8 @@ bool Inputs::get_check_for_nans() { // Checks to see if nan_test is needed // ----------------------------------------------------------------------- -bool Inputs::get_nan_test() { +bool Inputs::get_nan_test() +{ return get_setting_bool("Debug", "nan_test", "insert"); } @@ -1087,7 +1223,8 @@ bool Inputs::get_nan_test() { // Returns which variable is being tested for nans // ----------------------------------------------------------------------- -std::string Inputs::get_nan_test_variable() { +std::string Inputs::get_nan_test_variable() +{ return get_setting_str("Debug", "nan_test", "variable"); } @@ -1095,7 +1232,8 @@ std::string Inputs::get_nan_test_variable() { // Flag to have a latitude dependent radius, and by extension gravity // ----------------------------------------------------------------------- -bool Inputs::get_do_lat_dependent_radius() { +bool Inputs::get_do_lat_dependent_radius() +{ return get_setting_bool("Oblate", "isOblate"); } @@ -1103,7 +1241,8 @@ bool Inputs::get_do_lat_dependent_radius() { // Flag to include J2 term in the gravity calculation // ----------------------------------------------------------------------- -bool Inputs::get_do_J2() { +bool Inputs::get_do_J2() +{ return get_setting_bool("Oblate", "isJ2"); } @@ -1111,7 +1250,8 @@ bool Inputs::get_do_J2() { // // ----------------------------------------------------------------------- -json Inputs::get_initial_condition_types() { +json Inputs::get_initial_condition_types() +{ return get_setting_json("InitialConditions"); } @@ -1119,11 +1259,13 @@ json Inputs::get_initial_condition_types() { // // ----------------------------------------------------------------------- -json Inputs::get_boundary_condition_types() { +json Inputs::get_boundary_condition_types() +{ return get_setting_json("BoundaryConditions"); } -std::string Inputs::get_advection_neutrals_vertical() { +std::string Inputs::get_advection_neutrals_vertical() +{ return get_setting_str("Advection", "Neutrals", "Vertical"); } @@ -1131,6 +1273,7 @@ std::string Inputs::get_advection_neutrals_vertical() { // check to see if class is ok // -------------------------------------------------------------------------- -bool Inputs::is_ok() { +bool Inputs::is_ok() +{ return isOk; } From 562297adc8ee1c559ecbc7c0425eca816768ff47 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 16:41:57 -0400 Subject: [PATCH 112/127] [bug]: Rename NetCDF Output dimensions to not comflict with variables Netcdf files can't be ready by xarray (python) when a dimension has the same name as a variable. This is a problem for "z". Renames all `(x,y,z,time)` -> `(n_x, n_y, n_z, n_time)` > there are two output_netcdf files. I dont think the other is used? --- src/output_netcdf.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/output_netcdf.cpp b/src/output_netcdf.cpp index be1f560e..beff7c9c 100644 --- a/src/output_netcdf.cpp +++ b/src/output_netcdf.cpp @@ -68,7 +68,7 @@ void output_netcdf_3d(std::vector count_start, bool OutputContainer::read_container_netcdf() { bool didWork = true; - std::string whole_filename = directory + "/" + filename + ".nc"; + std::string whole_filename = directory + filename + ".nc"; std::string UNITS = "units"; try { @@ -161,17 +161,17 @@ bool OutputContainer::read_container_netcdf() { bool OutputContainer::write_container_netcdf() { bool didWork = true; - std::string whole_filename = directory + "/" + filename + ".nc"; + std::string whole_filename = directory+ filename + ".nc"; std::string UNITS = "units"; std::string LONG_NAME = "long_name"; try { NcFile ncdf_file(whole_filename, NcFile::replace); // Add dimensions: - NcDim xDim = ncdf_file.addDim("x", elements[0].value.n_rows); - NcDim yDim = ncdf_file.addDim("y", elements[0].value.n_cols); - NcDim zDim = ncdf_file.addDim("z", elements[0].value.n_slices); - NcDim tDim = ncdf_file.addDim("time", 1); + NcDim xDim = ncdf_file.addDim("n_x", elements[0].value.n_rows); + NcDim yDim = ncdf_file.addDim("n_y", elements[0].value.n_cols); + NcDim zDim = ncdf_file.addDim("n_z", elements[0].value.n_slices); + NcDim tDim = ncdf_file.addDim("n_time", 1); // Define the netCDF variables for the 3D data. // First create a vector of dimensions: From 06c9d7a9649517cd0ec94d40f2d65a973a865f25 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 17:48:10 -0400 Subject: [PATCH 113/127] [STY]: Dipole grid cleaning clean out old comments, improve comments that are gonna be left, refactor a bit. - Some nan's in lon are removed by changing the transform.cpp. It's not a bug so I included it in this commit. - Style in transform.coo too. --- src/init_mag_grid.cpp | 407 ++++++------------------------------------ src/transform.cpp | 106 +++++++---- 2 files changed, 124 insertions(+), 389 deletions(-) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 5661feb2..10d5cb63 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -334,264 +334,11 @@ std::pair Grid::qp_to_r_theta(precision_t q, precision } // ---------------------------------------------------------------------- -// Initialize the dipole grid. At the moment, Aaron B needs to update this string. -// #TODO: FIX THIS! -// Lon/Lat/Alt grid. The grid structure is general enough that each -// of the lon, lat, and alt can be a function of the other variables. +// Initialize the dipole grid. +// - inputs (min_apex, min_alt, LatStretch, FieldLineStretch, max_lat_dipole) +// are read from input files. And, of course, the numbers of each coordinate +// - nLats must be even!! // ---------------------------------------------------------------------- - -// void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) -// { - -// std::string function = "Grid::init_dipole_grid"; -// static int iFunction = -1; -// report.enter(function, iFunction); - -// // turn the switch on! -// IsGeoGrid = false; -// IsMagGrid = true; - -// int64_t iLon, iLat, iAlt; - -// report.print(0, "Creating Dipole Grid"); - -// report.print(3, "Getting mgrid_inputs inputs in dipole grid"); - -// Inputs::grid_input_struct grid_input = input.get_grid_inputs("ionGrid"); - -// // Get inputs -// report.print(3, "Setting inputs in dipole grid"); -// // Convert altitudes from km to m: -// precision_t min_apex = grid_input.min_apex * cKMtoM; -// precision_t min_alt = grid_input.alt_min * cKMtoM; -// precision_t LatStretch = grid_input.LatStretch; -// precision_t Gamma = grid_input.FieldLineStretch; -// precision_t max_lat = grid_input.max_lat_dipole; - -// // "Sanitize" the inputs -// precision_t planetRadius = planet.get_radius(0.0); -// precision_t min_lshell = (min_apex + planetRadius) / planetRadius; -// precision_t min_alt_re = (min_alt + planetRadius) / planetRadius; -// // precision_t max_r = (max_alt + planetRadius) / planetRadius; - -// // Get some coordinates and sizes in normalized coordinates: -// arma_vec lower_left_norm = quadtree.get_vect("LL"); -// arma_vec size_right_norm = quadtree.get_vect("SR"); -// arma_vec size_up_norm = quadtree.get_vect("SU"); - -// // ALB needs these for new grid -// precision_t qN, qS, Lon; - -// // LONGITUDES: -// precision_t dlon = size_right_norm(0) * cPI / (nLons - 2 * nGCs); -// precision_t lon0 = lower_left_norm(0) * cPI; -// arma_vec lon1d(nLons); -// // - Make a 1d vector -// // - copy it into the 3d cube -// for (iLon = 0; iLon < nLons; iLon++) -// { -// lon1d(iLon) = lon0 + (iLon - nGCs + 0.5) * dlon; -// } - -// for (iLat = 0; iLat < nLats; iLat++) -// { -// for (iAlt = 0; iAlt < nAlts; iAlt++) -// magLon_scgc.subcube(0, iLat, iAlt, nLons - 1, iLat, iAlt) = lon1d; -// } - -// geoLon_scgc = magLon_scgc; - -// // Base-latitudes: -// // some stretching is recommended (required 6??). -// // - Make a 1d vector -// // - copy it into the 3d cube - -// // precision_t dlat = size_up_norm(1) * cPI / (nLats - 2 * nGCs); -// // !QUESTION: Use dlat here or (max-min)/total ?? - -// precision_t lat0 = lower_left_norm(1) * cPI; -// arma_vec lat1d(nLats); - -// precision_t min_lat = get_lat_from_r_and_lshell(1.0, min_lshell); -// // max_lat defined earlier in inputs - -// // lay down spacing that's linear in cos^(1/latStretch) -// precision_t min_lat_, max_lat_; -// min_lat_ = cos(pow(min_lat, 1 / LatStretch)); -// max_lat_ = cos(pow(max_lat, 1 / LatStretch)); -// // see !QUESTION above -// precision_t dlat = (max_lat_ - min_lat_) / (nLats - 2 * nGCs); - -// for (iLat = nLats / 2; iLat < nLats; iLat++) -// { -// lat1d(iLat) = pow(acos(min_lat_ + (iLat - nGCs + 0.5) * dlat), -// LatStretch); -// // THIS WILL NOT WORK FOR OFFSET DIPOLES: -// lat1d(nLats - iLat - 1) = -lat1d(iLat); -// } - -// for (iLon = 0; iLon < nLons; iLon++) -// { -// for (iAlt = 0; iAlt < nAlts; iAlt++) -// { -// magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; -// } -// } - -// arma_vec rNorm1d(nAlts), lat1dAlong(nAlts); -// arma_cube r3d(nLons, nLats, nAlts); -// precision_t lShell; - -// rad_unit_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); - -// for (iLat = 0; iLat < nLats / 2; iLat++) -// { -// lat0 = lat1d(iLat); -// lat0 = cPI - lat0; - -// lShell = get_lshell(lat0, min_alt_re); - -// // with lShell, go thru and compute q (along field line) points at each longitude -// for (iLon = 0; iLon < nLons; iLon++) -// { -// Lon = magPhi_scgc(iLon, iLat, 1); - -// // This may not be working as expected: -// // auto Qvals = lshell_to_qn_qs(planet, lShell, Lon, min_alt); -// // qN = Qvals.first; -// // qS = Qvals.second; - -// qN = cos((cPI / 2 + lat0)) / pow(min_alt_re, 2.0); -// qS = cos((cPI / 2 - lat0)) / pow(min_alt_re, 2.0); -// // std:: cout<< qN<<" \t"< cPI / 2) -// // lat1dAlong(iAlt) = R_LAT_qline.first(iAlt); -// // if (lat1d(iLat) < -cPI / 2) -// lat1dAlong(iAlt) = R_LAT_qline.first(iAlt); -// rNorm1d(iAlt) = R_LAT_qline.second(iAlt); -// } -// // r3d.tube(iLon, iLat) = rNorm1d; -// r3d.tube(iLon, iLat) = rNorm1d - planetRadius; -// magLat_scgc.tube(iLon, iLat) = lat1dAlong; -// } -// } -// // return; -// // std::ofstream fout0; -// // fout0.open("grid0.csv"); -// // fout0 << "nl,nf,nz,lon,lat,alt,baselat\n"; -// // for (int ilarn = 0; ilarn < nLons; ilarn++) -// // { -// // for (int ilart = 0; ilart < nLats; ilart++) -// // { -// // for (int zi = 0; zi < nZ; zi++) -// // { -// // fout0 << ilarn << "," << ilart << "," << zi << "," << magLon_scgc(ilarn, ilart, zi) -// // << "," << magLat_scgc(ilarn, ilart, zi) << "," << r3d(ilarn, ilart, zi) << "\n"; -// // } -// // } -// // fout0.close(); - -// // return ; - -// geoLat_scgc = magLat_scgc; -// magAlt_scgc = r3d - planetRadius; -// geoAlt_scgc = magAlt_scgc; - -// // Calculate the radius, etc: -// fill_grid_radius(planet); - -// // Figure out what direction is radial: -// rad_unit_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); -// gravity_vcgc = make_cube_vector(nLons, nLats, nAlts, 3); - -// for (int iV = 0; iV < 3; iV++) -// { -// rad_unit_vcgc[iV].zeros(); -// gravity_vcgc[iV].zeros(); -// } - -// arma_cube br = 2 * sin(abs(magLat_scgc)); -// arma_cube bt = cos(magLat_scgc); -// arma_cube bm = sqrt(br % br + bt % bt); -// // Latitudinal direction of radial: -// arma_cube s = sign(magLat_scgc); -// s.elem(find(s == 0)).ones(); - -// rad_unit_vcgc[1] = bt / bm % s; -// rad_unit_vcgc[2] = -br / bm; - -// precision_t mu = planet.get_mu(); -// gravity_vcgc[1] = mu * rad_unit_vcgc[1] % radius2i_scgc; -// gravity_vcgc[2] = mu * rad_unit_vcgc[2] % radius2i_scgc; -// gravity_potential_scgc.set_size(nX, nY, nAlts); -// gravity_potential_scgc.zeros(); -// gravity_mag_scgc = sqrt( -// gravity_vcgc[0] % gravity_vcgc[0] + -// gravity_vcgc[1] % gravity_vcgc[1] + -// gravity_vcgc[2] % gravity_vcgc[2]); - -// std::vector llr, xyz, xyzRot1, xyzRot2; -// llr.push_back(magLon_scgc); -// llr.push_back(magLat_scgc); -// llr.push_back(r3d); -// xyz = transform_llr_to_xyz_3d(llr); - -// precision_t magnetic_pole_rotation = planet.get_dipole_rotation(); -// precision_t magnetic_pole_tilt = planet.get_dipole_tilt(); - -// // Reverse our dipole rotations: -// xyzRot1 = rotate_around_y_3d(xyz, magnetic_pole_tilt); -// xyzRot2 = rotate_around_z_3d(xyzRot1, magnetic_pole_rotation); - -// // transform back to lon, lat, radius: -// llr = transform_xyz_to_llr_3d(xyzRot2); - -// geoLon_scgc = llr[0]; -// geoLat_scgc = llr[1]; -// geoAlt_scgc = llr[2] - planetRadius; - -// std::ofstream fout; -// fout.open("grid.csv"); -// fout << "nl,nf,nz,lon,lat,alt,baselat\n"; -// for (int ilarn = 0; ilarn < nLons; ilarn++) -// { -// for (int ilart = 0; ilart < nLats; ilart++) -// { -// for (int zi = 0; zi < nZ; zi++) -// { -// fout << ilarn << "," << ilart << "," << zi << "," << geoLon_scgc(ilarn, ilart, zi) -// << "," << geoLat_scgc(ilarn, ilart, zi) << "," << geoAlt_scgc(ilarn, ilart, zi) << "\n"; -// } -// } -// fout.close(); - -// // return ; -// } - -// // for (iLon = 0; iLon < nLons; iLon++) -// // { -// // for (iAlt = 0; iAlt < nAlts; iAlt++) -// // { -// // magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; -// // } -// // } - -// calc_alt_grid_spacing(); - -// // Calculate magnetic field and magnetic coordinates: -// fill_grid_bfield(planet); - -// report.exit(function); -// return; -// } - void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) { @@ -603,6 +350,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) IsGeoGrid = false; IsMagGrid = true; + // Dimension iterators int64_t iLon, iLat, iAlt; report.print(0, "Creating Dipole Grid"); @@ -613,37 +361,32 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // Get inputs report.print(3, "Setting inputs in dipole grid"); - // Convert altitudes from km to m: precision_t min_apex = grid_input.min_apex * cKMtoM; precision_t min_alt = grid_input.alt_min * cKMtoM; precision_t LatStretch = grid_input.LatStretch; precision_t Gamma = grid_input.FieldLineStretch; precision_t max_lat = grid_input.max_lat_dipole; - // "Sanitize" the inputs + // Normalize to planet radius... precision_t planetRadius = planet.get_radius(0.0); + // L-Shell of minimum field line, normalized to planet radius precision_t min_lshell = (min_apex + planetRadius) / planetRadius; + // Altitude to begin modeling, normalized to planet radius precision_t min_alt_re = (min_alt + planetRadius) / planetRadius; - // precision_t max_r = (max_alt + planetRadius) / planetRadius; // Get some coordinates and sizes in normalized coordinates: arma_vec lower_left_norm = quadtree.get_vect("LL"); arma_vec size_right_norm = quadtree.get_vect("SR"); arma_vec size_up_norm = quadtree.get_vect("SU"); - // ALB needs these for new grid - precision_t qN, qS, Lon; - // LONGITUDES: + // - Make a 1d vector + // - copy it into the 3d cube precision_t dlon = size_right_norm(0) * cPI / (nLons - 2 * nGCs); precision_t lon0 = lower_left_norm(0) * cPI; arma_vec lon1d(nLons); - // - Make a 1d vector - // - copy it into the 3d cube for (iLon = 0; iLon < nLons; iLon++) - { lon1d(iLon) = lon0 + (iLon - nGCs + 0.5) * dlon; - } for (iLat = 0; iLat < nLats; iLat++) { @@ -653,140 +396,104 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) geoLon_scgc = magLon_scgc; - // Base-latitudes: - // some stretching is recommended (required 6??). - // - Make a 1d vector - // - copy it into the 3d cube - - // precision_t dlat = size_up_norm(1) * cPI / (nLats - 2 * nGCs); - // !QUESTION: Use dlat here or (max-min)/total ?? - - precision_t lat0 = lower_left_norm(1) * cPI; - arma_vec lat1d(nLats); + // LATITUDES + // min_latitude calculated from min_lshell (min_apex input) precision_t min_lat = get_lat_from_r_and_lshell(1.0, min_lshell); - ////////////////////////// - - // Lay down baseLat spacing according to an exponential factor: + // Lay down baseLat spacing according to an exponential factor. + // some intermediates: precision_t del_lat, blat_min_, blat_max_, tmp_lat; - // This is all going to be done on full field lines. split into halves later - int64_t nF = nLats/2, nZ = nAlts, nZby2 = nAlts / 2; + // Integers for field-line loops: + // - nF=nLats/2 (so nLarts MUST be even) + // - nZ=nAlts + // - nZby2 = nZ/2 + int64_t nF = nLats / 2, nZ = nAlts, nZby2 = nAlts / 2; + // lShells and baseLats are currently set for southern hemisphere then mirrored arma_vec Lshells(nF), baseLats(nF); blat_min_ = cos(pow(min_lat, 1.0 / LatStretch)); blat_max_ = cos(pow(max_lat, 1.0 / LatStretch)); - del_lat = (blat_max_ - blat_min_) / (nF - nGCs*2.0); + del_lat = (blat_max_ - blat_min_) / (nF - nGCs * 2.0); for (int i = 0; i < nF; i++) { // first put down "linear" spacing - tmp_lat = blat_min_ + del_lat * (i-nGCs+0.5); - // then scale it according to the exponent & convert back to deg + tmp_lat = blat_min_ + del_lat * (i - nGCs + 0.5); + // then scale it according to the exponent & acos it tmp_lat = pow(acos(tmp_lat), LatStretch); - // place values in array backwards, S => N hemis + // place values in array backwards, South pole -> equator. baseLats(nF - i - 1) = -tmp_lat; } // Find L-Shell for each baseLat // using L=R/sin2(theta), where theta is from north pole for (int i = 0; i < nF; i++) - { Lshells(i) = (min_alt_re) / pow(sin(cPI / 2 - baseLats(i)), 2.0); - } - - ////////////////// - // for (iLon = 0; iLon < nLons; iLon++) - // { - // for (iAlt = 0; iAlt < nAlts; iAlt++) - // { - // magLat_scgc.subcube(iLon, 0, iAlt, iLon, nLats - 1, iAlt) = lat1d; - // } - // } - - /////////// + // SPACING ALONG FIELD LINE // + // Coordinates along the field line to begin modeling + // - In dipole (p,q) coordinates + // - North & south hemisphere base + precision_t q_S, q_N; + // constant stride, scaled later + precision_t delqp; // allocate & calculate some things outside of the main loop - // fa, fb, fc are factors to make the code easier to read - precision_t q_S, q_N, delqp, fa, fb; - precision_t qp0, fb0, ft, delq, qp2; + // - mistly just factors to make the code easier to read + precision_t qp0, fb0, ft, delq, qp2, fa, fb, term0, term1, term2, term3, new_r; + // exp_q_dist is the fraction of total q distance to step for each pt along field line arma_vec exp_q_dist(nZ), q_vals(nZ); + // stored mag. coords temporarily in bAlts and bLats. arma_mat bAlts(nF, nZ), bLats(nF, nZ); - precision_t term0, term1, term2, term3, new_r; + // temp holding of results from q,p -> r,theta conversion: + std::pair r_theta; for (int i = 0; i < nZ; i++) - { exp_q_dist(i) = Gamma + (1 - Gamma) * exp(-pow(((i - nZby2) / (nZ / 10.0)), 2.0)); - } for (int i_nF = 0; i_nF < nF; i_nF++) { - // min/max q - q_S = - cos(cPI / 2 + baseLats(i_nF)) / pow(min_alt_re, 2.0); - q_N = -q_S;//cos(cPI / 2 + baseLats(i_nF)) / pow(min_alt_re, 2.0); - + q_S = -cos(cPI / 2 + baseLats(i_nF)) / pow(min_alt_re, 2.0); + q_N = -q_S; + // calculate const. stride similar to sami2/3 (huba & joyce 2000) // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == - // first loop for southern hemisphere, second for north. + // inlo loop thru southern hemisphere, mirror in north. for (int i_nZ = 0; i_nZ < nZ; i_nZ++) { - // std::cout< vector_in, int64_t nElements, - precision_t *array_out) { + precision_t *array_out) +{ for (int64_t i = 0; i < nElements; i++) array_out[i] = vector_in[i]; @@ -58,16 +60,20 @@ void copy_vector_to_array(std::vector vector_in, // ----------------------------------------------------------------------- void copy_cube_to_array(arma_cube cube_in, - precision_t *array_out) { + precision_t *array_out) +{ int64_t nX = cube_in.n_rows; int64_t nY = cube_in.n_cols; int64_t nZ = cube_in.n_slices; int64_t iX, iY, iZ, index; - for (iX = 0; iX < nX; iX++) { - for (iY = 0; iY < nY; iY++) { - for (iZ = 0; iZ < nZ; iZ++) { + for (iX = 0; iX < nX; iX++) + { + for (iY = 0; iY < nY; iY++) + { + for (iZ = 0; iZ < nZ; iZ++) + { index = iX * nY * nZ + iY * nZ + iZ; array_out[index] = cube_in(iX, iY, iZ); } @@ -81,14 +87,17 @@ void copy_cube_to_array(arma_cube cube_in, void copy_mat_to_array(arma_mat mat_in, precision_t *array_out, - bool isFortran) { + bool isFortran) +{ int64_t nX = mat_in.n_rows; int64_t nY = mat_in.n_cols; int64_t iX, iY, index; - for (iX = 0; iX < nX; iX++) { - for (iY = 0; iY < nY; iY++) { + for (iX = 0; iX < nX; iX++) + { + for (iY = 0; iY < nY; iY++) + { if (isFortran) index = iY * nX + iX; else @@ -111,14 +120,17 @@ void copy_mat_to_array(arma_mat mat_in, void copy_array_to_mat(precision_t *array_in, arma_mat &mat_out, - bool isFortran) { + bool isFortran) +{ int64_t nX = mat_out.n_rows; int64_t nY = mat_out.n_cols; int64_t iX, iY, index; - for (iX = 0; iX < nX; iX++) { - for (iY = 0; iY < nY; iY++) { + for (iX = 0; iX < nX; iX++) + { + for (iY = 0; iY < nY; iY++) + { if (isFortran) index = iY * nX + iX; else @@ -134,28 +146,32 @@ void copy_array_to_mat(precision_t *array_in, // ----------------------------------------------------------------------- // Calculate the magnitude of a arma_cube vector // ----------------------------------------------------------------------- -arma_cube calc_magnitude(std::vector xyz) { +arma_cube calc_magnitude(std::vector xyz) +{ arma_cube r = sqrt(xyz[0] % xyz[0] + xyz[1] % xyz[1] + xyz[2] % xyz[2]); - return r; + return r; } // ----------------------------------------------------------------------- -// Transform X, Y, Z to +// Transform X, Y, Z to // Longitude (llr[0]), Latitude (llr[1]), Radius (llr[2]) // Use armidillo cubes // ----------------------------------------------------------------------- -std::vector transform_xyz_to_llr_3d(std::vector xyz) { +std::vector transform_xyz_to_llr_3d(std::vector xyz) +{ std::vector llr; arma_cube xy, r, lon; r = calc_magnitude(xyz); xy = sqrt(xyz[0] % xyz[0] + xyz[1] % xyz[1]); - lon = acos(xyz[0]/xy); + lon = acos(xyz[0] / xy); uvec ind_ = find(xyz[1] < 0.0); lon.elem(ind_) = 2 * cPI - lon.elem(ind_); + ind_ = find(xyz[1] > 2 * cPI); + lon.elem(ind_) = lon.elem(ind_) + 2 * cPI; llr.push_back(lon); llr.push_back(asin(xyz[2] / r)); llr.push_back(r); @@ -168,7 +184,8 @@ std::vector transform_xyz_to_llr_3d(std::vector xyz) { // Use armidillo cubes // ----------------------------------------------------------------------- -std::vector transform_llr_to_xyz_3d(std::vector llr) { +std::vector transform_llr_to_xyz_3d(std::vector llr) +{ std::vector xyz; xyz.push_back(llr[2] % cos(llr[1]) % cos(llr[0])); xyz.push_back(llr[2] % cos(llr[1]) % sin(llr[0])); @@ -180,7 +197,8 @@ std::vector transform_llr_to_xyz_3d(std::vector llr) { // Transform Longitude, Latitude, Radius to X, Y, Z // ----------------------------------------------------------------------- -void transform_llr_to_xyz(precision_t llr_in[3], precision_t xyz_out[3]) { +void transform_llr_to_xyz(precision_t llr_in[3], precision_t xyz_out[3]) +{ // llr_in[0] = longitude (in radians) // llr_in[1] = latitude (in radians) // llr_in[2] = radius @@ -195,7 +213,8 @@ void transform_llr_to_xyz(precision_t llr_in[3], precision_t xyz_out[3]) { // ----------------------------------------------------------------------- std::vector rotate_around_z_3d(std::vector XYZ_in, - precision_t angle) { + precision_t angle) +{ arma_cube X = XYZ_in[0]; arma_cube Y = XYZ_in[1]; @@ -205,7 +224,7 @@ std::vector rotate_around_z_3d(std::vector XYZ_in, precision_t ca = cos(angle); precision_t sa = sin(angle); - XYZ_out.push_back( X * ca + Y * sa); + XYZ_out.push_back(X * ca + Y * sa); XYZ_out.push_back(-X * sa + Y * ca); XYZ_out.push_back(Z); @@ -218,7 +237,8 @@ std::vector rotate_around_z_3d(std::vector XYZ_in, // ----------------------------------------------------------------------- std::vector rotate_around_y_3d(std::vector XYZ_in, - precision_t angle) { + precision_t angle) +{ arma_cube X = XYZ_in[0]; arma_cube Y = XYZ_in[1]; @@ -241,7 +261,8 @@ std::vector rotate_around_y_3d(std::vector XYZ_in, // ----------------------------------------------------------------------- std::vector rotate_around_x_3d(std::vector XYZ_in, - precision_t angle) { + precision_t angle) +{ arma_cube X = XYZ_in[0]; arma_cube Y = XYZ_in[1]; @@ -252,7 +273,7 @@ std::vector rotate_around_x_3d(std::vector XYZ_in, precision_t sa = sin(angle); XYZ_out.push_back(X); - XYZ_out.push_back( Y * ca + Z * sa); + XYZ_out.push_back(Y * ca + Z * sa); XYZ_out.push_back(-Y * sa + Z * ca); return XYZ_out; @@ -264,12 +285,13 @@ std::vector rotate_around_x_3d(std::vector XYZ_in, // ----------------------------------------------------------------------- void transform_rot_z(precision_t xyz_in[3], precision_t angle_in, - precision_t xyz_out[3]) { + precision_t xyz_out[3]) +{ precision_t ca = cos(angle_in); precision_t sa = sin(angle_in); - xyz_out[0] = xyz_in[0] * ca + xyz_in[1] * sa; + xyz_out[0] = xyz_in[0] * ca + xyz_in[1] * sa; xyz_out[1] = -xyz_in[0] * sa + xyz_in[1] * ca; - xyz_out[2] = xyz_in[2]; + xyz_out[2] = xyz_in[2]; } // ----------------------------------------------------------------------- @@ -278,7 +300,8 @@ void transform_rot_z(precision_t xyz_in[3], precision_t angle_in, // ----------------------------------------------------------------------- void transform_rot_y(precision_t xyz_in[3], precision_t angle_in, - precision_t xyz_out[3]) { + precision_t xyz_out[3]) +{ precision_t ca = cos(angle_in); precision_t sa = sin(angle_in); xyz_out[0] = xyz_in[0] * ca - xyz_in[2] * sa; @@ -291,7 +314,8 @@ void transform_rot_y(precision_t xyz_in[3], precision_t angle_in, // ----------------------------------------------------------------------- void transform_float_vector_to_array(std::vector input, - precision_t output[3]) { + precision_t output[3]) +{ for (int i = 0; i < 3; i++) output[i] = input[i]; } @@ -303,14 +327,15 @@ void transform_float_vector_to_array(std::vector input, void transform_vector_xyz_to_env(precision_t xyz_in[3], precision_t lon, precision_t lat, - precision_t env_out[3]) { + precision_t env_out[3]) +{ - env_out[2] = xyz_in[0] * cos(lat) * cos(lon) + - xyz_in[1] * cos(lat) * sin(lon) + xyz_in[2] * sin(lat); + env_out[2] = xyz_in[0] * cos(lat) * cos(lon) + + xyz_in[1] * cos(lat) * sin(lon) + xyz_in[2] * sin(lat); env_out[1] = -(xyz_in[0] * sin(lat) * cos(lon) + xyz_in[1] * sin(lat) * sin(lon) - xyz_in[2] * cos(lat)); - env_out[0] = - xyz_in[0] * sin(lon) + + env_out[0] = -xyz_in[0] * sin(lon) + xyz_in[1] * cos(lon); } @@ -329,7 +354,8 @@ void transform_vector_xyz_to_env(precision_t xyz_in[3], void vector_diff(precision_t vect_in_1[3], precision_t vect_in_2[3], - precision_t vect_out[3]) { + precision_t vect_out[3]) +{ for (int i = 0; i < 3; i++) vect_out[i] = vect_in_1[i] - vect_in_2[i]; } @@ -340,6 +366,8 @@ void vector_diff(precision_t vect_in_1[3], void vector_add(precision_t vect_in_1[3], precision_t vect_in_2[3], - precision_t vect_out[3]) { - for (int i = 0; i < 3; i++) vect_out[i] = vect_in_1[i] + vect_in_2[i]; + precision_t vect_out[3]) +{ + for (int i = 0; i < 3; i++) + vect_out[i] = vect_in_1[i] + vect_in_2[i]; } From 3233b5dfaa3d7fd1ee86b74598be48aca4187402 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 17:53:50 -0400 Subject: [PATCH 114/127] [FEAT]: Add dipole grid options to json settings files - not all ionGrid options are used for sphere/dipole grid. I'll add a doc page on this in the next commit... --- share/run/UA/inputs/defaults.json | 7 +++++-- share/run/aether.json | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/share/run/UA/inputs/defaults.json b/share/run/UA/inputs/defaults.json index b0cae02f..4e85093a 100644 --- a/share/run/UA/inputs/defaults.json +++ b/share/run/UA/inputs/defaults.json @@ -85,8 +85,11 @@ "nLonsPerBlock" : 36, "nAlts" : 200, "MinAlt" : 80.0, - "MinApex" : 120.0, - "MaxAlt" : 5000.0}, + "MinApex" : 120.0, + "MaxAlt" : 5000.0, + "LatMax":88.0, + "LineSpacing":0.2, + "LatStretch":6.0}, "Oblate" : { "isOblate" : false, diff --git a/share/run/aether.json b/share/run/aether.json index a819f82e..261bbc36 100644 --- a/share/run/aether.json +++ b/share/run/aether.json @@ -25,10 +25,16 @@ "dAltScale" : 0.25, "IsUniformAlt" : false}, - "ionGrid" : { - "AltFile" : "", - "IsUniformAlt" : false, - "dAltScale" : 0.5}, + "ionGrid": { + "dAltScale": 0.2, + "LatStretch": 6, + "Shape": "dipole", + "nLatsPerBlock" : 22, + "nAlts":159, + "LatMax":88, + "MinAlt":100.0, + "MinApex":150.0 + }, "OmniwebFiles" : ["UA/inputs/omni_20110319.txt"], From 70802b86f78efb3d6dd2dcb5c512006d43bad838 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 18:10:42 -0400 Subject: [PATCH 115/127] [doc] Draft up some words on the dipole grid. ! I removed the section about passing btwn N/S hemispheres, since that's undecided. --- doc/internals/grid.md | 63 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/doc/internals/grid.md b/doc/internals/grid.md index a9858803..ad91f315 100644 --- a/doc/internals/grid.md +++ b/doc/internals/grid.md @@ -18,20 +18,19 @@ again, 16 processors are needed, etc. However, in the altitude/radial direction, the number of points that are specified in the aether.json is unchanged, as it does not rely on the number of processors used. -- [Grids in Aether](#grids-in-aether) - - [Grid Types Explained](#grid-types-explained) - - [Grid Shapes Explained](#grid-shapes-explained) - - [TL;DR](#tldr) - - [The Sphere Grid](#the-sphere-grid) - - [The Cubesphere Grid](#the-cubesphere-grid) - - [The Dipole Grid](#the-dipole-grid) - - [Root Nodes](#root-nodes) - - [Sphere](#sphere) - - [Cubesphere](#cubesphere) - - [Specifying Root Nodes](#specifying-root-nodes) - - [Specifying the Grid](#specifying-the-grid) - - [Horizontal Resolution](#horizontal-resolution) - - [Vertical Resolution](#vertical-resolution) +- [Grid Types Explained](#grid-types-explained) +- [Grid Shapes Explained](#grid-shapes-explained) + - [TL;DR](#tldr) + - [The Sphere Grid](#the-sphere-grid) + - [The Cubesphere Grid](#the-cubesphere-grid) + - [The Dipole Grid](#the-dipole-grid) + - [Root Nodes](#root-nodes) + - [Sphere](#sphere) + - [Cubesphere](#cubesphere) + - [Specifying Root Nodes](#specifying-root-nodes) +- [Specifying the Grid](#specifying-the-grid) + - [Horizontal Resolution](#horizontal-resolution) + - [Vertical Resolution](#vertical-resolution) ## Grid Types Explained @@ -115,24 +114,24 @@ bottom of the field-line. Each fieldline starts at the lowest modeled altitude and curves towards the equator. In the northern hemisphere, this means that the fieldlines curve south, while in the southern hemisphere they curve north. -The latitudinal spacing is such that there is a dependence on the L-shell (i.e., -the equatorial radial extent of the field-line). Along the `k` dimension, -field-lines either terminate when they reach the equatorial plane, forming half -of a full field-line, or they terminate at the highest point specified in the -aether.json file, forming an open field line. Any grid point with an L-shell -less than the peak altitude will terminate in the equatorial plane, while any -field-line that has an L-shell above the peak altitude will simply terminate. -Field-lines that terminate in the equatorial plane have corresponding -field-lines in the other hemisphere, so ghostcells are used to pass information -back and forth. Field-lines that terminate at the maximum altitude have -vertical boundary conditions set in the ghost cells. - -The transition from the "closed" field-line region to the "open" field-line -region is a natural break point in the grid. The transition between these -regions can be handled with ghostcells in the "latitudinal" direction. -Therefore it makes the most sense to have have 4 distinct regions in "latitude": -south open, south closed, north closed, north open. The message passing is -treated differently at the boundaries between each of these regions. +The latitudinal spacing is determined by the `LatStretch` factor in the settings. +The base latitudes are then scaled in such a way that **higher** `LatStretch` leads to +more points near the equator, 1.0 is roughly linear, and then values less than 1.0 will +distribute more points near the poles. The exact spacing is calculated where the +difference between successive values is proportional to: +`cos(lat_max)^(1/LatStretch)`. Using an even number of latitudes is required. + +Along the `k` dimension, field lines terminate after a specified number of points. +When using the Dipole grid option, there is not an option to set the maximum altitude. +Rather, points are laid down from the pole towards the equator, stopping when the field +line reaches the equator. Ghost cells are then used to pass information across the +equator. The spacing of points along each field line is the same as +[(Huba, Joyce & Fedder, 2000)](https://doi.org/10.1029/2000JA000035). +Using an even number of points along the `k` dimension (`nAlts`) means that no points +will lie on the magnetic equator and thus the field lines from the high latitude +regions will not reach beyond the plasmasphere. See +[the dipole script in edu/examples](../../edu/examples/Dipole/dipole.py) to +experiment with the available options. ### Root Nodes From d8f79a550ff109ca53016ef1cb20acbc41276613 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 18:31:12 -0400 Subject: [PATCH 116/127] [FEAT]: Experimental check for nLats & attempt to run in 1D > Probably won't work. But a decent starting point, I hope. Feel free to revert this commit if it's broken. --- src/init_mag_grid.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 10d5cb63..ec02cb28 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -412,6 +412,18 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // lShells and baseLats are currently set for southern hemisphere then mirrored arma_vec Lshells(nF), baseLats(nF); + // now make sure the user used 1 or an even number for nLats + if (nLats % 2 != 0) + { + if (nLats == 1) + { + report.print(0, ">> Running in 1D. Experinental!!"); + nF = 1; + } + else + report.error("Cannot use odd nLats with dipole grid!"); + } + blat_min_ = cos(pow(min_lat, 1.0 / LatStretch)); blat_max_ = cos(pow(max_lat, 1.0 / LatStretch)); del_lat = (blat_max_ - blat_min_) / (nF - nGCs * 2.0); From c295433be9fcd9e319a549c952d255cc33433aa3 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Thu, 26 Sep 2024 20:57:23 -0400 Subject: [PATCH 117/127] [BUG] Revert commit that screwed up longitudes Though I could do something clever. It's wasn't. Glad I caught this, geez --- src/transform.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/transform.cpp b/src/transform.cpp index 252511b9..74a2f494 100644 --- a/src/transform.cpp +++ b/src/transform.cpp @@ -170,8 +170,6 @@ std::vector transform_xyz_to_llr_3d(std::vector xyz) lon = acos(xyz[0] / xy); uvec ind_ = find(xyz[1] < 0.0); lon.elem(ind_) = 2 * cPI - lon.elem(ind_); - ind_ = find(xyz[1] > 2 * cPI); - lon.elem(ind_) = lon.elem(ind_) + 2 * cPI; llr.push_back(lon); llr.push_back(asin(xyz[2] / r)); llr.push_back(r); From f58fda32c4de4b4c649226a3fa39ab6ee822f4e0 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Sat, 28 Sep 2024 12:34:38 -0400 Subject: [PATCH 118/127] [BUG]: Fix altitude spacing in dipole grid - Grid spacing was not exponential, as expected. The issue was with using nZ vs nAlts & is fixed. --- src/init_mag_grid.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index ec02cb28..f26c5dfa 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -408,7 +408,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // - nF=nLats/2 (so nLarts MUST be even) // - nZ=nAlts // - nZby2 = nZ/2 - int64_t nF = nLats / 2, nZ = nAlts, nZby2 = nAlts / 2; + int64_t nF = nLats / 2, nZ = nAlts*2, nZby2 = nAlts; // lShells and baseLats are currently set for southern hemisphere then mirrored arma_vec Lshells(nF), baseLats(nF); @@ -462,7 +462,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // temp holding of results from q,p -> r,theta conversion: std::pair r_theta; - for (int i = 0; i < nZ; i++) + for (int i = 0; i < nAlts; i++) exp_q_dist(i) = Gamma + (1 - Gamma) * exp(-pow(((i - nZby2) / (nZ / 10.0)), 2.0)); for (int i_nF = 0; i_nF < nF; i_nF++) @@ -474,7 +474,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // calculate const. stride similar to sami2/3 (huba & joyce 2000) // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == // inlo loop thru southern hemisphere, mirror in north. - for (int i_nZ = 0; i_nZ < nZ; i_nZ++) + for (int i_nZ = 0; i_nZ < nAlts; i_nZ++) { // Should be using lshell_to_qn_qs, but it wasn't working right. // This does the same, but won't work for offset dipoles. From 051420889a14ca065345d20ba874aeef4a4a4781 Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Sat, 28 Sep 2024 12:37:59 -0400 Subject: [PATCH 119/127] [MAINT]: rm unnecessary file & func's unused funcs rm'ed from in init_mag_grid file for debugging removed from edu/examples/dipole/ --- edu/examples/Dipole/grid.cpp | 167 ------------------- src/init_mag_grid.cpp | 300 ----------------------------------- 2 files changed, 467 deletions(-) delete mode 100644 edu/examples/Dipole/grid.cpp diff --git a/edu/examples/Dipole/grid.cpp b/edu/examples/Dipole/grid.cpp deleted file mode 100644 index 9e018acf..00000000 --- a/edu/examples/Dipole/grid.cpp +++ /dev/null @@ -1,167 +0,0 @@ - -#include -#include -#include -#include - -using arma_vec = arma::Col; -using arma_mat = arma::Mat; - -inline double deg2rad(double degrees) -{ - // function compiled inline to convert degrees to radians - static const double pi_on_180 = 4.0 * atan(1.0) / 180.0; - return degrees * pi_on_180; -} - -inline double rad2deg(double degrees) -{ - // function compiled inline to convert degrees to radians - static const double pi_on_180 = 4.0 * atan(1.0) / 180.0; - return degrees / pi_on_180; - // return degrees; -} - -double qp_solve(double q, double p) -{ - double term0 = 256.0 / 27.0 * pow(q, 2.0) * pow(p, 4.0); - double term1 = pow((1.0 + sqrt(1.0 + term0)), 2.0 / 3.0); - double term2 = pow(term0, 1.0 / 3.0); - double term3 = 0.5 * pow(((pow(term1, 2) + term1 * term2 + pow(term2, 2)) / term1), 3.0 / 2.0); - double new_r = p * (4.0 * term3) / (1.0 + term3) / (1.0 + sqrt(2.0 * term3 - 1.0)); - return new_r; -} - -int main() -{ - int nF = 100; - int nZ = 200; - float alt_min = 90.0; - float min_blat = 2.25; - float max_blat = 89.9; - - // user-defined constants - double gams = 0.2; - double baselat_spacing = 6.0; - - // constants - int iErr = 0; - float Re = 6371.0; - float pi = 3.14159; - float pio2 = pi / 2.0; - bool didWork = true; - - int nFby2 = nF / 2; - int nZby2 = nZ / 2; - double altmin_inRe = (alt_min + Re) / Re; - - // outputs - arma_mat bLats(nF, nZ), bLons(nF, nZ), bAlts(nF, nZ); - - arma_vec baseLats(nF); - //, bLons(nF, nZ), bAlts(nF, nZ); - - // TODO: REFACTOR FROM HERE TO **1 ? - // Lay down baseLat spacing according to an exponential factor: - double del_lat, blat_min_, blat_max_, tmp_lat; - blat_min_ = cos(deg2rad(pow(min_blat, 1.0 / baselat_spacing))); - blat_max_ = cos(deg2rad(pow(max_blat, 1.0 / baselat_spacing))); - del_lat = (blat_max_ - blat_min_) / (nF - 1.0); - - for (int i = 0; i < nF; i++) - { - // first put down "linear" spacing - tmp_lat = blat_min_ + del_lat * i; - // then scale it according to the exponent & convert back to deg - tmp_lat = pow(rad2deg(acos(tmp_lat)), baselat_spacing); - // place values in array backwards, S => N hemis - baseLats(nF - i - 1) = -tmp_lat; - } - - // Find L-Shell for each baseLat - // using L=R/sin2(theta), where theta is from north pole - arma_vec Lshells(nF); - for (int i = 0; i < nF; i++) - { - Lshells(i) = ((alt_min + Re) / Re) / pow(sin(deg2rad(90.0 - baseLats(i))), 2.0); - } - - // allocate & calculate some things outside of the main loop - // fa, fb, fc are factors to make the code easier to read - double q_S, q_N, delqp, fb; - double qp0, fb0, ft, delq, qp2; - arma_vec exp_q_dist(nZ), q_vals(nZ); - - for (int i = 0; i < nZ; i++) - { - exp_q_dist(i) = gams + (1 - gams) * exp(-pow(((i - nZby2) / (nZ / 10.0)), 2.0)); - } - - for (int i_nF = 0; i_nF < nF; i_nF++) - { - - // min/max q - q_N = cos(deg2rad(90.0 + baseLats(i_nF))) / pow((alt_min + Re) / Re, 2.0); - q_S = cos(deg2rad(90 - baseLats(i_nF))) / pow((alt_min + Re) / Re, 2.0); - - // calculate const. stride similar to sami2/3 (huba & joyce 2000) - // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == - // first loop for southern hemisphere, second for north. - for (int i_nZ = 0; i_nZ < nZby2; i_nZ++) - { - delqp = (q_N - q_S) / nZ; - qp0 = q_S + i_nZ * (delqp); - delqp = altmin_inRe * delqp; - fb0 = (1 - exp_q_dist(i_nZ)) / exp(-q_S / delqp - 1); - ft = exp_q_dist(i_nZ) - fb0 + fb0 * exp(-(qp0 - q_S) / delqp); - - delq = qp0 - q_S; - qp2 = q_S + ft * delq; - - bAlts(i_nF, i_nZ) = qp_solve(qp2, Lshells(i_nF)); - bLats(i_nF, i_nZ) = rad2deg(asin(qp2 * pow(bAlts(i_nF, i_nZ), 2.0))); - - // test mirroring across hemi's - - bAlts(i_nF, nZ - i_nZ - 1) = qp_solve(-qp2, Lshells(i_nF)); - bLats(i_nF, nZ - i_nZ - 1) = -bLats(i_nF, i_nZ); - } - } - - // calculate const. stride similar to sami2/3 (huba & joyce 2000) - // == >> sinh(gamma*qi)/sinh(gamma*q_S) << == - // first loop for southern hemisphere, second for north. - // for (int i_nZ=nZ; i_nZ>nZby2; i_nZ++){ - // delqp = (q_N - q_S)/nZ; - // qp0 = q_S + i_nZ*(delqp); - // delqp = altmin_inRe * delqp; - // fb0 = (1-exp_q_dist(i_nZ)) / exp(-q_S/delqp - 1); - // fa = exp_q_dist(i_nZ) - fb0; - // ft = fa + fb0 * exp(-(qp0 - q_S)/delqp); - // - // delq = qp0 - q_S; - // qp2 = q_S + ft * delq; - ////fb = (exp_q_dist(i_nZ) - fa) + fa * exp(-(exp_q_dist(i_nZ) - q_S) / delqp); - ////fb = (exp_q_dist(i_nZ) - fa) + fa * exp(-(q_S + i_nZ * delqp/altmin_inRe) / i_nZ); - // bAlts(i_nF, i_nZ) = qp_solve(qp2, Lshells(i_nF)); - // bLats(i_nF, i_nZ) = rad2deg(asin(qp2 * pow(bAlts(i_nF, i_nZ), 2.0))); - // } - // } - //} - //} - //} - - std::ofstream fout; - fout.open("grid.csv"); - fout << "nf,nz,lat,alt,baselat\n"; - for (int fi = 0; fi < nF; fi++) - { - for (int zi = 0; zi < nZ; zi++) - { - fout << fi << "," << zi << "," << bLats(fi, zi) << "," << bAlts(fi, zi) << "," << baseLats(fi) << "\n"; - } - } - fout.close(); - - return iErr; -} diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index f26c5dfa..5f57e33a 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -6,306 +6,6 @@ #include "../include/aether.h" -// ---------------------------------------------------------------------- -// Routine to find q_N and q_S for a given L -// -// ---------------------------------------------------------------------- -std::pair Grid::lshell_to_qn_qs(Planets planet, - precision_t Lshell, - precision_t Lon, - precision_t AltMin) -{ - std::string function = "Grid::lshell_to_qn_qs"; - static int iFunction = -1; - report.enter(function, iFunction); - - precision_t qN, qS; - - precision_t XyzDipoleLeft[3], XyzDipoleMid[3], XyzDipoleRight[3]; - precision_t XyzGeoLeft[3], XyzGeoMid[3], XyzGeoRight[3]; - precision_t rGeoLeft, rGeoMid, rGeoRight; - precision_t LlrDipoleLeft[3], LlrDipoleMid[3], LlrDipoleRight[3]; - precision_t ThetaTilt, PhiTilt; - precision_t Lat, Radius, rMin; - // Named dimension constants - static int Lon_ = 0, Lat_ = 1, Radius_ = 2; - - // bound vars for bisection search - precision_t ThetaRight, ThetaLeft, ThetaMid; - precision_t rDipoleLeft, rDipoleMid, rDipoleRight; - - // Stopping condition for bisection search - precision_t DeltaTheta; - precision_t Tolerance = 1e-4; - - // status vars for bisection search - int iStatusLeft, iStatusRight, iStatusMid; - // note we normalize Lshell by equatorial radius - precision_t RadiusEq = planet.get_radius(0.0); - - // loop for qN and qS - for (int iQ = 0; iQ < 2; iQ++) - { - - if (iQ == 0) - { - // set initial left, mid, right bounds for bisection search for qN - ThetaRight = 0.5 * cPI; - ThetaLeft = 1.0 * cDtoR; - ThetaMid = 0.5 * (ThetaRight + ThetaLeft); - } - else - { - // set initial left, mid, right bounds for bisection search for qS - ThetaLeft = 0.5 * cPI; - ThetaRight = 179.0 * cDtoR; - ThetaMid = 0.5 * (ThetaRight + ThetaLeft); - } - - // Initial stopping condition stopping condition - DeltaTheta = abs(ThetaLeft - ThetaRight); - - // start bisection search for qN - while (DeltaTheta > Tolerance) - { - - // find rDipole that cooresponds to these Left,Mid,Right - // ThetaDipole values - rDipoleLeft = Lshell * pow(sin(ThetaLeft), 2.0); - rDipoleMid = Lshell * pow(sin(ThetaMid), 2.0); - rDipoleRight = Lshell * pow(sin(ThetaRight), 2.0); - - // Compute XyzDipole for left, mid,right states - LlrDipoleLeft[Lon_] = Lon; - LlrDipoleLeft[Lat_] = 0.5 * cPI - ThetaLeft; - LlrDipoleLeft[Radius_] = rDipoleLeft; - transform_llr_to_xyz(LlrDipoleLeft, XyzDipoleLeft); - - LlrDipoleMid[Lon_] = Lon; - LlrDipoleMid[Lat_] = 0.5 * cPI - ThetaMid; - LlrDipoleMid[Radius_] = rDipoleMid; - transform_llr_to_xyz(LlrDipoleMid, XyzDipoleMid); - - LlrDipoleRight[Lon_] = Lon; - LlrDipoleRight[Lat_] = 0.5 * cPI - ThetaRight; - LlrDipoleRight[Radius_] = rDipoleRight; - transform_llr_to_xyz(LlrDipoleRight, XyzDipoleRight); - - // Transform to XyzGeo and unnormalize - convert_dipole_geo_xyz(planet, XyzDipoleLeft, XyzGeoLeft); - convert_dipole_geo_xyz(planet, XyzDipoleMid, XyzGeoMid); - convert_dipole_geo_xyz(planet, XyzDipoleRight, XyzGeoRight); - - // cout << "XyzGeoLeft[0]" << XyzGeoLeft[0] << endl; - // cout << "XyzGeoLeft[1]" << XyzGeoLeft[1] << endl; - // cout << "XyzGeoLeft[2]" << XyzGeoLeft[2] << endl; - - XyzGeoLeft[0] = XyzGeoLeft[0] * RadiusEq; - XyzGeoLeft[1] = XyzGeoLeft[1] * RadiusEq; - XyzGeoLeft[2] = XyzGeoLeft[2] * RadiusEq; - - // abort; - - XyzGeoMid[0] = XyzGeoMid[0] * RadiusEq; - XyzGeoMid[1] = XyzGeoMid[1] * RadiusEq; - XyzGeoMid[2] = XyzGeoMid[2] * RadiusEq; - - XyzGeoRight[0] = XyzGeoRight[0] * RadiusEq; - XyzGeoRight[1] = XyzGeoRight[1] * RadiusEq; - XyzGeoRight[2] = XyzGeoRight[2] * RadiusEq; - - // Compute radius in geo coordinate for comparison to rmin - rGeoLeft = sqrt(pow(XyzGeoLeft[0], 2) + pow(XyzGeoLeft[1], 2) + pow(XyzGeoLeft[2], 2)); - rGeoMid = sqrt(pow(XyzGeoMid[0], 2) + pow(XyzGeoMid[1], 2) + pow(XyzGeoMid[2], 2)); - rGeoRight = sqrt(pow(XyzGeoRight[0], 2) + pow(XyzGeoRight[1], 2) + pow(XyzGeoRight[2], 2)); - - // get rmin for given latitude. Radius is lat dependent in general. - // also find status in (0) or out (1) of rMin - Lat = 0.5 * cPI - acos(XyzGeoLeft[2] / rGeoLeft); - Radius = planet.get_radius(Lat); - rMin = Radius + AltMin; - if (rGeoLeft < rMin) - { - iStatusLeft = 0; - } - else - { - iStatusLeft = 1; - } - - Lat = 0.5 * cPI - acos(XyzGeoMid[2] / rGeoMid); - Radius = planet.get_radius(Lat); - rMin = Radius + AltMin; - if (rGeoMid < rMin) - { - iStatusMid = 0; - } - else - { - iStatusMid = 1; - } - - Lat = 0.5 * cPI - acos(XyzGeoRight[2] / rGeoRight); - Radius = planet.get_radius(Lat); - rMin = Radius + AltMin; - if (rGeoRight < rMin) - { - iStatusRight = 0; - } - else - { - iStatusRight = 1; - } - - // Use status values to update left, right and mid values of theta - if (iStatusMid == 0) - { - if (iStatusRight == 1) - { - // Mid becomes left and right stays right - ThetaLeft = ThetaMid; - ThetaMid = 0.5 * (ThetaLeft + ThetaRight); - } - else - { - // Mid becomes right and left stays left - ThetaRight = ThetaMid; - ThetaMid = 0.5 * (ThetaLeft + ThetaRight); - } - } - else - { - if (iStatusRight == 0) - { - // Mid becomes left and right stays right - ThetaLeft = ThetaMid; - ThetaMid = 0.5 * (ThetaLeft + ThetaRight); - } - else - { - // Mid becomes right and left stays left - ThetaRight = ThetaMid; - ThetaMid = 0.5 * (ThetaLeft + ThetaRight); - } - } - // Update stopping condition - DeltaTheta = abs(ThetaLeft - ThetaRight); - } - - // set the q value - rDipoleMid = Lshell * pow(sin(ThetaMid), 2.0); - if (iQ == 0) - { - qN = pow(rDipoleMid, -2.0) * cos(ThetaMid); - // cout << "!!! For L = " << Lshell << endl; - // cout << "!!! qN = " << qN << endl; - // cout << "!!! ThetaMid = " << ThetaMid*cRtoD << endl; - } - else - { - qS = pow(rDipoleMid, -2.0) * cos(ThetaMid); - // cout << "!!! qS = " << qS << endl; - } - } - - report.exit(function); - return {qN, qS}; -} - -// ----------------------------------------------------------------------- -// Convert XyzDipole to XyzGeo -// -// ----------------------------------------------------------------------- - -void Grid::convert_dipole_geo_xyz(Planets planet, precision_t XyzDipole[3], precision_t XyzGeo[3]) -{ - precision_t XyzRemoveShift[3]; - precision_t XyzRemoveTilt[3]; - precision_t XyzRemoveRot[3]; - - // get planetary parameters, use radius at equator for Lshell reference - precision_t magnetic_pole_tilt = planet.get_dipole_tilt(); - precision_t magnetic_pole_rotation = planet.get_dipole_rotation(); - precision_t radius = planet.get_radius(0.0); - - // get the dipole shift, but normalize it to equatorial radius - precision_t dipole_center[3]; - std::vector temp_dipole_center = planet.get_dipole_center(); - transform_float_vector_to_array(temp_dipole_center, dipole_center); - - dipole_center[0] = dipole_center[0] / radius; - dipole_center[1] = dipole_center[1] / radius; - dipole_center[2] = dipole_center[2] / radius; - - // Remove Tilt - transform_rot_y(XyzDipole, magnetic_pole_tilt, XyzRemoveTilt); - - // Remove Rot - transform_rot_z(XyzRemoveTilt, magnetic_pole_rotation, XyzRemoveRot); - - // Remove Shift - vector_add(XyzRemoveRot, dipole_center, XyzGeo); - - // cout << "XyzDipole[0]" << XyzDipole[0] << endl; - // cout << "XyzDipole[1]" << XyzDipole[1] << endl; - // cout << "XyzDipole[2]" << XyzDipole[2] << endl; - // - // cout << "XyzGeo[0]" << XyzGeo[0] << endl; - // cout << "XyzGeo[1]" << XyzGeo[1] << endl; - // cout << "XyzGeo[2]" << XyzGeo[2] << endl; -} - -// ---------------------------------------------------------------------- -// Routine to fill in the q values for a particular L and lon -// using equations 7-8 from Huba et al 2000 -// ---------------------------------------------------------------------- -std::pair Grid::fill_dipole_q_line(precision_t qN_, - precision_t qS_, - precision_t Gamma_, - int64_t nZ_, - precision_t lShell_, - precision_t min_alt_) - -{ - - std::string function = "Grid::fill_dipole_q_line"; - static int iFunction = -1; - report.enter(function, iFunction); - - precision_t r, theta; - precision_t Dx; - precision_t Llr[3], Xyz[3], q[nZ_]; - // precision_t Radius, rMin, alt_min, , delqp, qp0, fb0, ft; - precision_t delqp, qp0, fb0, ft, delq; - int64_t nZby2 = nZ_ / 2; - arma_vec exp_q_dist(nZ_), r_vals(nZ_), lat_vals(nZ_); - - for (int i = 0; i < nZ_; i++) - { - exp_q_dist(i) = Gamma_ + (1 - Gamma_) * exp(-pow(((i - nZby2) / (nZ_ / 10.0)), 2.0)); - } - for (int i_nZ = 0; i_nZ < nZ_; i_nZ++) - { - delqp = (qN_ - qS_) / nZ_; - qp0 = qS_ + i_nZ * (delqp); - delqp = min_alt_ * delqp; - fb0 = (1 - exp_q_dist(i_nZ)) / exp(-qS_ / delqp - 1); - ft = exp_q_dist(i_nZ) - fb0 + fb0 * exp(-(qp0 - qS_) / delqp); - - delq = qp0 - qS_; - q[i_nZ] = qS_ + ft * delq; - - auto rtheta = qp_to_r_theta(-q[i_nZ], lShell_); - r_vals[i_nZ] = rtheta.first; - // r_vals[i_nZ + nZby2] = rtheta.first; - lat_vals[i_nZ] = rtheta.second; - // lat_vals[i_nZ + nZby2] = -rtheta.second; - } - - report.exit(function); - return {r_vals, lat_vals}; -} - // ---------------------------------------------------------------------- // Routine to convert p and q to r and theta. Can be solved iteratively, // or with approach from (Swisdak, 2006), who solved it analytically: From b9de1be981edcdb2568e6c4c487aab37c616d84e Mon Sep 17 00:00:00 2001 From: Aaron Bukowski Date: Mon, 30 Sep 2024 18:19:23 -0400 Subject: [PATCH 120/127] [FEAT]: Support ghost cells in dipole grid - I can't test in 1D since things break in the geographic grid. If the dipole grid is broken in 1D, let me know! The altitude (along-field-line) ghost cells are treated as standard grid points. - At low altitudes, points are close enough this is OK. - At high altitudes, it's going to be hard to get ghost cells. - For example, if they extend along the field line, there will be points at +25 Re (L-shell of max_lat) - If they are scaled to lower radius, or just not as far along the field line, they will not be perpendicular to B anymore! > Maybe specify where field lines should be separated btwn open/closed? Then closed field lines will overlap across equator, and open field lines will not be perpendicular to B in the ghost cells? IDK yet! ---- Ghost cells in latitude currently go over the poles. Discussion is happening on whether this is a good idea or not. --- --- src/init_mag_grid.cpp | 49 ++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/init_mag_grid.cpp b/src/init_mag_grid.cpp index 5f57e33a..e5d3164b 100644 --- a/src/init_mag_grid.cpp +++ b/src/init_mag_grid.cpp @@ -59,6 +59,9 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) Inputs::grid_input_struct grid_input = input.get_grid_inputs("ionGrid"); + // Number of ghost cells: + int64_t nGCs = get_nGCs(); + // Get inputs report.print(3, "Setting inputs in dipole grid"); precision_t min_apex = grid_input.min_apex * cKMtoM; @@ -85,6 +88,11 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) precision_t dlon = size_right_norm(0) * cPI / (nLons - 2 * nGCs); precision_t lon0 = lower_left_norm(0) * cPI; arma_vec lon1d(nLons); + + // Quick 1D check for longitudes: + // Same as geo_grid here... + if (!HasXdim) dlon = 1.0 * cDtoR; + for (iLon = 0; iLon < nLons; iLon++) lon1d(iLon) = lon0 + (iLon - nGCs + 0.5) * dlon; @@ -96,7 +104,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) geoLon_scgc = magLon_scgc; - // LATITUDES + // LATITUDES: // min_latitude calculated from min_lshell (min_apex input) precision_t min_lat = get_lat_from_r_and_lshell(1.0, min_lshell); @@ -105,38 +113,44 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // some intermediates: precision_t del_lat, blat_min_, blat_max_, tmp_lat; // Integers for field-line loops: - // - nF=nLats/2 (so nLarts MUST be even) - // - nZ=nAlts - // - nZby2 = nZ/2 - int64_t nF = nLats / 2, nZ = nAlts*2, nZby2 = nAlts; + // - nF=nLats/2 (so nLats MUST be even) + // - nZ=nAlts*2 - (we fill up HALF field lines) + // - nZby2 = nAlts + // -> Plus, experinemtal support for altitude ghost cells... + int64_t nF = (nLats) / 2 , nZ = (nAlts) * 2, nZby2 = (nAlts); // lShells and baseLats are currently set for southern hemisphere then mirrored arma_vec Lshells(nF), baseLats(nF); + blat_min_ = cos(pow(min_lat, 1.0 / LatStretch)); + blat_max_ = cos(pow(max_lat, 1.0 / LatStretch)); + del_lat = (blat_max_ - blat_min_) / (nF - nGCs * 2.0); + // now make sure the user used 1 or an even number for nLats if (nLats % 2 != 0) { - if (nLats == 1) + if (!HasYdim) { - report.print(0, ">> Running in 1D. Experinental!!"); + del_lat = 1.0 * cDtoR; + report.print(0, "Running in single latitude dimension. Experinental!!"); nF = 1; } else report.error("Cannot use odd nLats with dipole grid!"); } - blat_min_ = cos(pow(min_lat, 1.0 / LatStretch)); - blat_max_ = cos(pow(max_lat, 1.0 / LatStretch)); - del_lat = (blat_max_ - blat_min_) / (nF - nGCs * 2.0); - + // loop over all cells - everything including the ghost cells + // -> This means some points will go over the pole (baseLat > +- 90 degrees) + // They're taken care of in the conversion to geographic coordinates. for (int i = 0; i < nF; i++) { // first put down "linear" spacing tmp_lat = blat_min_ + del_lat * (i - nGCs + 0.5); - // then scale it according to the exponent & acos it + // then scale it according to the exponent & acos tmp_lat = pow(acos(tmp_lat), LatStretch); // place values in array backwards, South pole -> equator. baseLats(nF - i - 1) = -tmp_lat; } + report.print(3, "Done setting base latitudes for dipole grid."); // Find L-Shell for each baseLat // using L=R/sin2(theta), where theta is from north pole @@ -176,8 +190,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) // inlo loop thru southern hemisphere, mirror in north. for (int i_nZ = 0; i_nZ < nAlts; i_nZ++) { - // Should be using lshell_to_qn_qs, but it wasn't working right. - // This does the same, but won't work for offset dipoles. + // This won't work for offset dipoles. // Doesn't have any lat/lon dependence. delqp = (q_N - q_S) / (nZ + 1); qp0 = q_S + i_nZ * (delqp); @@ -194,6 +207,7 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) bLats(i_nF, i_nZ) = r_theta.second; } } + report.print(3, "Done generating q-spacing for dipole grid."); arma_vec rNorm1d(nAlts), lat1dAlong(nAlts); arma_cube r3d(nLons, nLats, nAlts); @@ -218,11 +232,13 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) magLat_scgc.tube(iLon, nLats - iLat - 1) = -lat1dAlong; } } + report.print(3, "Done generating symmetric latitude & altitude spacing in dipole."); geoLat_scgc = magLat_scgc; magAlt_scgc = r3d - planetRadius; geoAlt_scgc = magAlt_scgc; + report.print(4, "Beginning coordinate transformations of the dipole grid."); // Calculate the radius, of planet fill_grid_radius(planet); @@ -256,6 +272,8 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) gravity_vcgc[1] % gravity_vcgc[1] + gravity_vcgc[2] % gravity_vcgc[2]); + report.print(4, "Done gravity calculations for the dipole grid."); + std::vector llr, xyz, xyzRot1, xyzRot2; llr.push_back(magLon_scgc); llr.push_back(magLat_scgc); @@ -275,11 +293,14 @@ void Grid::init_dipole_grid(Quadtree quadtree, Planets planet) geoLon_scgc = llr[0]; geoLat_scgc = llr[1]; geoAlt_scgc = llr[2] - planetRadius; + report.print(4, "Done dipole -> geographic transformations for the dipole grid."); calc_alt_grid_spacing(); + report.print(4, "Done altitude spacing for the dipole grid."); // Calculate magnetic field and magnetic coordinates: fill_grid_bfield(planet); + report.print(4, "Done filling dipole grid with b-field!"); report.exit(function); return; From f1782686e47c2eca8efdf4dd167c0474337902a3 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:14:44 -0400 Subject: [PATCH 121/127] MERGE: useSeconds is not used anymore --- include/times.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/times.h b/include/times.h index 5496e799..f082f31e 100644 --- a/include/times.h +++ b/include/times.h @@ -99,7 +99,7 @@ class Times { \param useSeconds if false, replace seconds with 0 **/ - std::string get_YMD_HMS(bool useSeconds); + std::string get_YMD_HMS(); /************************************************************** \brief Returns the current time as a string (year, month...) From bf7aebfd1329308c3d63b2a129a755084fe12b4e Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:15:19 -0400 Subject: [PATCH 122/127] MEGRE: calc_conduction is done in energy stuff --- src/advance.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/advance.cpp b/src/advance.cpp index 6887694d..66d83b56 100644 --- a/src/advance.cpp +++ b/src/advance.cpp @@ -141,9 +141,6 @@ bool advance(Planets &planet, calc_aurora(gGrid, neutrals, ions); calc_aurora(mGrid, neutralsMag, ionsMag); - // Calculate some neutral source terms: - neutrals.calc_conduction(gGrid, time); - // Calculate chemistry on both grids: chemistry.calc_chemistry(neutrals, ions, time, gGrid); chemistryMag.calc_chemistry(neutralsMag, ionsMag, time, mGrid); From 25be8e0622a80c9af52b709ac93db44149ed8d21 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:16:00 -0400 Subject: [PATCH 123/127] MERGE: get_nz can return with and without GCs --- src/calc_dt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calc_dt.cpp b/src/calc_dt.cpp index 5854eadf..d1c45e3f 100644 --- a/src/calc_dt.cpp +++ b/src/calc_dt.cpp @@ -138,7 +138,7 @@ precision_t calc_dt_vertical(Grid grid, std::vector cMax_vcgc) { report.enter(function, iFunction); precision_t dt; - if (input.get_nAltsGeo() > 1) { + if (grid.get_nZ(false) > 1) { arma_cube dtz = grid.dalt_center_scgc / cMax_vcgc[2]; dt = dtz.min(); } else @@ -146,4 +146,4 @@ precision_t calc_dt_vertical(Grid grid, std::vector cMax_vcgc) { report.exit(function); return dt; -} \ No newline at end of file +} From 7eeeb94cabb8f21e4648078578482e313333e5ce Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:16:25 -0400 Subject: [PATCH 124/127] MERGE: get_nz can return with and without GCs --- src/ions_bcs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ions_bcs.cpp b/src/ions_bcs.cpp index aae7cc73..4166f2e9 100644 --- a/src/ions_bcs.cpp +++ b/src/ions_bcs.cpp @@ -30,7 +30,7 @@ bool Ions::set_bcs(Grid grid, bool didWork = true; - if (input.get_nAltsGeo() > 1) { + if (grid.get_nZ(false) > 1) { didWork = set_lower_bcs(grid, time, indices); if (didWork) From cc839182fdb0ad57083ee1bbdec0e210426d0ff7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:17:13 -0400 Subject: [PATCH 125/127] MERGE: 00 seconds handled somewhere else --- src/time.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/time.cpp b/src/time.cpp index fa986916..cbef9756 100644 --- a/src/time.cpp +++ b/src/time.cpp @@ -147,11 +147,8 @@ double Times::get_end() { // Get the current time as a string // ----------------------------------------------------------------------------- -std::string Times::get_YMD_HMS(bool useSeconds) { - if (useSeconds) - return sYMD_HMS; - else - return sYMD_HM0; +std::string Times::get_YMD_HMS() { + return sYMD_HMS; } // ----------------------------------------------------------------------------- From 681764e94914ce28dbd34cdd6c12fd47471f4f89 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:18:13 -0400 Subject: [PATCH 126/127] MERGE: check takes a string now --- src/main/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/main.cpp b/src/main/main.cpp index 75d8decd..9c0a8286 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -128,7 +128,7 @@ int main() { } if (input.get_check_for_nans()) { - didWork = neutrals.check_for_nonfinites(); + didWork = neutrals.check_for_nonfinites("After Inputs"); didWork = ions.check_for_nonfinites(); } From eaad347cc3adbc995187bfbd4e5d5aac386d5dc7 Mon Sep 17 00:00:00 2001 From: Aaron Ridley Date: Wed, 9 Oct 2024 20:33:27 -0400 Subject: [PATCH 127/127] BUG: * should be % in armacube mult --- src/fill_grid.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fill_grid.cpp b/src/fill_grid.cpp index de5bcfd1..5cd90393 100644 --- a/src/fill_grid.cpp +++ b/src/fill_grid.cpp @@ -376,17 +376,17 @@ void Grid::calc_alt_grid_spacing() { MeshH3 = h1 + h2 + h3; MeshH4 = h1 + h2 + h3 + h4; MeshCoef1s3rdp1.slice(iAlt) = - -1.0*( MeshH2*MeshH3*MeshH4 + MeshH1*MeshH3*MeshH4 + - MeshH1*MeshH2*MeshH4 + MeshH1*MeshH2*MeshH3)/ - (MeshH1*MeshH2*MeshH3*MeshH4); + -1.0*( MeshH2 % MeshH3 % MeshH4 + MeshH1 % MeshH3 % MeshH4 + + MeshH1 % MeshH2 % MeshH4 + MeshH1 % MeshH2 % MeshH3)/ + (MeshH1 % MeshH2 % MeshH3 % MeshH4); MeshCoef1s3rdp2.slice(iAlt) = - 1.0*( MeshH2*MeshH3*MeshH4)/(h1*h2*(h2 + h3)*(h2 + h3 + h4)); + 1.0*( MeshH2 % MeshH3 % MeshH4)/(h1 % h2 % (h2 + h3) % (h2 + h3 + h4)); MeshCoef1s3rdp3.slice(iAlt) = - -1.0*( MeshH1*MeshH3*MeshH4)/(MeshH2*h2*h3*(h3+h4)); + -1.0*( MeshH1 % MeshH3 % MeshH4)/(MeshH2 % h2 % h3 % (h3+h4)); MeshCoef1s3rdp4.slice(iAlt) = - 1.0*( MeshH1*MeshH2*MeshH4)/(MeshH3*(h3+h2)*h3*h4); + 1.0*( MeshH1 % MeshH2 % MeshH4)/(MeshH3 % (h3+h2) % h3 % h4); MeshCoef1s3rdp5.slice(iAlt) = - -1.0*( MeshH1*MeshH2*MeshH3)/(MeshH4*(h2+h3+h4)*(h3+h4)*h4); + -1.0*( MeshH1 % MeshH2 % MeshH3)/(MeshH4 % (h2+h3+h4) % (h3+h4) % h4); } }