diff --git a/tests/geometryRTheta/advection_2d_rp/advection_all_tests.cpp b/tests/geometryRTheta/advection_2d_rp/advection_all_tests.cpp index c0e65418b..d8ca24e97 100644 --- a/tests/geometryRTheta/advection_2d_rp/advection_all_tests.cpp +++ b/tests/geometryRTheta/advection_2d_rp/advection_all_tests.cpp @@ -86,14 +86,14 @@ struct SimulationParameters LogicalToPseudoPhysicalMapping const& pseudo_cart_map, AnalyticalPhysicalToLogicalMapping const& rev_map, AnalyticalLogicalToPhysicalMapping const& a_map, - std::string m_name, - std::string dom_name) + std::string const& map_name, + std::string const& dom_name) : to_physical_mapping_host(map_host) , to_physical_mapping(map) , to_logical_mapping(rev_map) , analytical_to_pseudo_physical_mapping(pseudo_cart_map) , analytical_to_physical_mapping(a_map) - , mapping_name(m_name) + , mapping_name(map_name) , domain_name(dom_name) { } @@ -105,7 +105,7 @@ struct NumericalMethodParameters TimeStepper time_stepper; double time_step; std::string method_name; - NumericalMethodParameters(TimeStepper&& time_stepper, double step, std::string name) + NumericalMethodParameters(TimeStepper&& time_stepper, double step, std::string const& name) : time_stepper(std::move(time_stepper)) , time_step(step) , method_name(name) @@ -119,7 +119,7 @@ struct NumericalParams IdxRangeRTheta const grid; double const dt; - NumericalParams(IdxRangeRTheta grid, double dt) : grid(grid), dt(dt) {}; + NumericalParams(IdxRangeRTheta grid, double dt) : grid(grid), dt(dt) {} NumericalParams(NumericalParams&& params) = default; NumericalParams(NumericalParams& params) = default; }; @@ -215,17 +215,25 @@ void run_simulations_with_methods( << to_lower(num.method_name) << "-"; std::string output_stem = output_stream.str(); - simulate_the_3_simulations( + SplinePolarFootFinder const foot_finder( + num.time_stepper, + sim.to_physical_mapping, + sim.analytical_to_pseudo_physical_mapping, + params.advection_builder, + params.advection_evaluator); + + BslAdvectionRTheta + advection_operator(params.interpolator, foot_finder, sim.to_physical_mapping); + + run_simulations( sim.to_physical_mapping_host, sim.to_physical_mapping, sim.to_logical_mapping, sim.analytical_to_pseudo_physical_mapping, sim.analytical_to_physical_mapping, params.grid, - num.time_stepper, - params.interpolator, - params.advection_builder, - params.advection_evaluator, + foot_finder, + advection_operator, params.final_time, num.time_step, params.if_save_curves, diff --git a/tests/geometryRTheta/advection_2d_rp/advection_selected_test.cpp b/tests/geometryRTheta/advection_2d_rp/advection_selected_test.cpp index 24e62131e..cb364d641 100644 --- a/tests/geometryRTheta/advection_2d_rp/advection_selected_test.cpp +++ b/tests/geometryRTheta/advection_2d_rp/advection_selected_test.cpp @@ -268,17 +268,18 @@ int main(int argc, char** argv) // SELECTION OF THE SIMULATION. #if defined(TRANSLATION_SIMULATION) - TranslationSimulation simulation(to_physical_mapping_host, rmin, rmax); + AdvectionSimulation simulation + = get_translation_simulation(to_physical_mapping_host, rmin, rmax); std::string const simu_type = "TRANSLATION"; key += "Translation"; #elif defined(ROTATION_SIMULATION) - RotationSimulation simulation(to_physical_mapping_host, rmin, rmax); + AdvectionSimulation simulation = get_rotation_simulation(to_physical_mapping_host, rmin, rmax); std::string const simu_type = "ROTATION"; key += "Rotation"; #elif defined(DECENTRED_ROTATION_SIMULATION) - DecentredRotationSimulation simulation(to_physical_mapping_host); + AdvectionSimulation simulation = get_decentred_rotation_simulation(to_physical_mapping_host); std::string const simu_type = "DECENTRED ROTATION"; key += "Decentred_rotation"; #endif @@ -288,6 +289,15 @@ int main(int argc, char** argv) fs::create_directory(output_folder); } + SplinePolarFootFinder const foot_finder( + time_stepper, + to_physical_mapping, + logical_to_pseudo_cart_mapping, + builder, + spline_evaluator_extrapol); + + BslAdvectionRTheta advection_operator(interpolator, foot_finder, to_physical_mapping); + std::cout << mapping_name << " MAPPING - " << adv_domain_name << " DOMAIN - " << method_name << " - " << simu_type << " : " << std::endl; simulate( @@ -297,11 +307,9 @@ int main(int argc, char** argv) logical_to_pseudo_cart_mapping, to_physical_analytical_mapping, grid, - time_stepper, + foot_finder, + advection_operator, simulation, - interpolator, - builder, - spline_evaluator_extrapol, final_time, dt, save_curves, diff --git a/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp b/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp index a2860035c..65c4e70a5 100644 --- a/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp +++ b/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp @@ -330,7 +330,8 @@ template < class PhysicalToLogicalMapping, class LogicalToPseudoPhysicalMapping, class AnalyticalLogicalToPhysicalMapping, - class TimeStepper, + class PolarFootFinder, + class AdvectionOperator, class Simulation> void simulate( LogicalToPhysicalMappingHost const& to_physical_mapping_host, @@ -339,32 +340,15 @@ void simulate( LogicalToPseudoPhysicalMapping const& analytical_to_pseudo_physical_mapping, AnalyticalLogicalToPhysicalMapping const& analytical_to_physical_mapping, IdxRangeRTheta const& grid, - TimeStepper const& time_stepper, + PolarFootFinder const& foot_finder, + AdvectionOperator const& advection_operator, Simulation& simulation, - PreallocatableSplineInterpolatorRTheta const& - function_interpolator, - SplineRThetaBuilder const& advection_builder, - SplineRThetaEvaluatorConstBound& advection_evaluator, double const final_time, double const dt, bool save_curves, bool save_feet, std::string const& output_folder) { - SplinePolarFootFinder const foot_finder( - time_stepper, - to_physical_mapping, - analytical_to_pseudo_physical_mapping, - advection_builder, - advection_evaluator); - - BslAdvectionRTheta - advection_operator(function_interpolator, foot_finder, to_physical_mapping_host); - auto function_to_be_advected_test = simulation.get_test_function(); - auto advection_field_test = simulation.get_advection_field(); - - - // TO CLOCK THE SIMULATION ------------------------------------------------------------------ std::chrono::time_point start_simulation; std::chrono::time_point end_simulation; @@ -391,7 +375,7 @@ void simulate( if (ddc::get(coord) <= 1e-15) { ddc::get(coord) = 0; } - allfdistribu_test(irp) = function_to_be_advected_test(coord); + allfdistribu_test(irp) = simulation.advected_function(coord); }); @@ -400,7 +384,7 @@ void simulate( ddc::for_each(grid, [&](IdxRTheta const irp) { // Moving the coordinates in the physical domain: CoordXY const coord_xy = to_physical_mapping_host(ddc::coordinate(irp)); - CoordXY const advection_field = advection_field_test(coord_xy, 0.); + CoordXY const advection_field = simulation.advection_field(coord_xy, 0.); // Define the advection field on the physical domain: ddcHelper::get(advection_field_test_vec_host)(irp) = ddc::get(advection_field); @@ -433,13 +417,13 @@ void simulate( grid, analytical_to_physical_mapping, to_logical_mapping, - advection_field_test, + simulation.advection_field, end_time); feet_coords_rp_dt = compute_exact_feet_rp( grid, analytical_to_physical_mapping, to_logical_mapping, - advection_field_test, + simulation.advection_field, dt); @@ -448,7 +432,7 @@ void simulate( ddc::for_each(grid, [&](IdxRTheta const irp) { double const err = fabs(allfdistribu_advected_test(irp) - - function_to_be_advected_test(feet_coords_rp_end_time(irp))); + - simulation.advected_function(feet_coords_rp_end_time(irp))); max_err = max_err > err ? max_err : err; }); @@ -464,7 +448,7 @@ void simulate( to_physical_mapping_host, grid, allfdistribu_advected_test, - function_to_be_advected_test, + simulation.advected_function, get_field(feet_coords_rp_end_time)) << std::endl; @@ -502,10 +486,10 @@ void simulate( host_t initial_function(grid); host_t end_function(grid); ddc::for_each(grid, [&](const IdxRTheta irp) { - initial_function(irp) = function_to_be_advected_test(ddc::coordinate(irp)); + initial_function(irp) = simulation.advected_function(ddc::coordinate(irp)); // Exact final state - end_function(irp) = function_to_be_advected_test(feet_coords_rp_end_time(irp)); + end_function(irp) = simulation.advected_function(feet_coords_rp_end_time(irp)); }); saving_computed(to_physical_mapping_host, get_field(initial_function), name_0); saving_computed(to_physical_mapping_host, get_field(end_function), name_1); @@ -578,19 +562,17 @@ template < class PhysicalToLogicalMapping, class LogicalToPseudoPhysicalMapping, class AnalyticalLogicalToPhysicalMapping, - class TimeStepper> -void simulate_the_3_simulations( + class PolarFootFinder, + class AdvectionOperator> +void run_simulations( LogicalToPhysicalMappingHost const& to_physical_mapping_host, LogicalToPhysicalMapping const& to_physical_mapping, PhysicalToLogicalMapping const& to_logical_mapping, LogicalToPseudoPhysicalMapping const& analytical_to_pseudo_physical_mapping, AnalyticalLogicalToPhysicalMapping const& analytical_to_physical_mapping, IdxRangeRTheta const& grid, - TimeStepper& time_stepper, - PreallocatableSplineInterpolatorRTheta const& - function_interpolator, - SplineRThetaBuilder const& advection_builder, - SplineRThetaEvaluatorConstBound& advection_evaluator, + PolarFootFinder const& foot_finder, + AdvectionOperator const& advection_operator, double const final_time, double const dt, bool const& save_curves, @@ -602,19 +584,22 @@ void simulate_the_3_simulations( double const rmin = ddc::coordinate(r_idx_range.front()); double const rmax = ddc::coordinate(r_idx_range.back()); - TranslationSimulation simulation_1(to_physical_mapping, rmin, rmax); - RotationSimulation simulation_2(to_physical_mapping, rmin, rmax); - DecentredRotationSimulation simulation_3(to_physical_mapping); + AdvectionSimulation simulation_translation + = get_translation_simulation(to_physical_mapping, rmin, rmax); + AdvectionSimulation simulation_rotation + = get_rotation_simulation(to_physical_mapping, rmin, rmax); + AdvectionSimulation simulation_decentred_rotation + = get_decentred_rotation_simulation(to_physical_mapping); - std::string const title_simu_1 = " TRANSLATION : "; - std::string const title_simu_2 = " ROTATION : "; - std::string const title_simu_3 = " DECENTRED ROTATION : "; + std::string const title_simu_translation = " TRANSLATION : "; + std::string const title_simu_rotation = " ROTATION : "; + std::string const title_simu_decentred_rotation = " DECENTRED ROTATION : "; std::string output_folder = output_stem + "Translation_output"; if (save_curves or save_feet) { fs::create_directory(output_folder); } - std::cout << title + title_simu_1 << std::endl; + std::cout << title + title_simu_translation << std::endl; simulate( to_physical_mapping_host, to_physical_mapping, @@ -622,11 +607,9 @@ void simulate_the_3_simulations( analytical_to_pseudo_physical_mapping, analytical_to_physical_mapping, grid, - time_stepper, - simulation_1, - function_interpolator, - advection_builder, - advection_evaluator, + foot_finder, + advection_operator, + simulation_translation, final_time, dt, save_curves, @@ -637,7 +620,7 @@ void simulate_the_3_simulations( if (save_curves or save_feet) { fs::create_directory(output_folder); } - std::cout << title + title_simu_2 << std::endl; + std::cout << title + title_simu_rotation << std::endl; simulate( to_physical_mapping_host, to_physical_mapping, @@ -645,11 +628,9 @@ void simulate_the_3_simulations( analytical_to_pseudo_physical_mapping, analytical_to_physical_mapping, grid, - time_stepper, - simulation_2, - function_interpolator, - advection_builder, - advection_evaluator, + foot_finder, + advection_operator, + simulation_rotation, final_time, dt, save_curves, @@ -660,7 +641,7 @@ void simulate_the_3_simulations( if (save_curves or save_feet) { fs::create_directory(output_folder); } - std::cout << title + title_simu_3 << std::endl; + std::cout << title + title_simu_decentred_rotation << std::endl; simulate( to_physical_mapping_host, to_physical_mapping, @@ -668,11 +649,9 @@ void simulate_the_3_simulations( analytical_to_pseudo_physical_mapping, analytical_to_physical_mapping, grid, - time_stepper, - simulation_3, - function_interpolator, - advection_builder, - advection_evaluator, + foot_finder, + advection_operator, + simulation_decentred_rotation, final_time, dt, save_curves, diff --git a/tests/geometryRTheta/advection_2d_rp/test_cases.hpp b/tests/geometryRTheta/advection_2d_rp/test_cases.hpp index 337577cb5..40716187e 100644 --- a/tests/geometryRTheta/advection_2d_rp/test_cases.hpp +++ b/tests/geometryRTheta/advection_2d_rp/test_cases.hpp @@ -21,30 +21,9 @@ */ - -// TEST FUNCTIONS ----------------------------------------------------------------- -/** - * @brief Base class for the test functions for the 2D polar advection operator. - * - * @see BslAdvectionRTheta - * @see AdvectionSimulation - */ -class FunctionToBeAdvected -{ -public: - virtual ~FunctionToBeAdvected() = default; - - /** - * @brief Get the value of the function. - * - * @param[in] coord - * The coordinate where we want to evaluate the function. - * - * @return The value of the function at the coordinate. - */ - virtual double operator()(CoordRTheta coord) = 0; -}; - +//--------------------------------------------------------------------------------- +// TEST FUNCTIONS +//--------------------------------------------------------------------------------- /** * @brief Test function for the 2D polar advection operator. @@ -61,7 +40,7 @@ class FunctionToBeAdvected * */ template -class FunctionToBeAdvected_cos_4_elipse : public FunctionToBeAdvected +class FunctionToBeAdvected_cos_4_elipse { private: Mapping const& m_mapping; @@ -75,7 +54,20 @@ class FunctionToBeAdvected_cos_4_elipse : public FunctionToBeAdvected */ explicit FunctionToBeAdvected_cos_4_elipse(Mapping const& mapping) : m_mapping(mapping) {} - double operator()(CoordRTheta coord_rp) final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION FunctionToBeAdvected_cos_4_elipse( + FunctionToBeAdvected_cos_4_elipse const&) + = default; + + /** + * @brief Get the value of the function. + * + * @param[in] coord_rp + * The coordinate where we want to evaluate the function. + * + * @return The value of the function at the coordinate. + */ + KOKKOS_FUNCTION double operator()(CoordRTheta coord_rp) const { CoordXY const coord_xy(m_mapping(coord_rp)); double const x = ddc::get(coord_xy); @@ -85,11 +77,11 @@ class FunctionToBeAdvected_cos_4_elipse : public FunctionToBeAdvected double const x_bar = 0.; double const y_bar = 0.; - double const r_1 = std::sqrt((x - x_bar) * (x - x_bar) + 8 * (y - y_bar) * (y - y_bar)); - double const r_2 = std::sqrt(8 * (x - x_bar) * (x - x_bar) + (y - y_bar) * (y - y_bar)); + double const r_1 = Kokkos::sqrt((x - x_bar) * (x - x_bar) + 8 * (y - y_bar) * (y - y_bar)); + double const r_2 = Kokkos::sqrt(8 * (x - x_bar) * (x - x_bar) + (y - y_bar) * (y - y_bar)); - double const G_1 = std::pow(std::cos(M_PI * r_1 / 2. / a), 4) * (r_1 < a); - double const G_2 = std::pow(std::cos(M_PI * r_2 / 2. / a), 4) * (r_2 < a); + double const G_1 = ipow(Kokkos::cos(M_PI * r_1 / 2. / a), 4) * (r_1 < a); + double const G_2 = ipow(Kokkos::cos(M_PI * r_2 / 2. / a), 4) * (r_2 < a); return 1. / 2. * (G_1 + G_2); } @@ -105,7 +97,7 @@ class FunctionToBeAdvected_cos_4_elipse : public FunctionToBeAdvected * */ template -class FunctionToBeAdvected_gaussian : public FunctionToBeAdvected +class FunctionToBeAdvected_gaussian { private: Mapping const& m_mapping; @@ -158,7 +150,20 @@ class FunctionToBeAdvected_gaussian : public FunctionToBeAdvected { } - double operator()(CoordRTheta coord_rp) final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION FunctionToBeAdvected_gaussian( + FunctionToBeAdvected_gaussian const&) + = default; + + /** + * @brief Get the value of the function. + * + * @param[in] coord_rp + * The coordinate where we want to evaluate the function. + * + * @return The value of the function at the coordinate. + */ + KOKKOS_FUNCTION double operator()(CoordRTheta coord_rp) const { // Gaussian centered in (x0, y0): CoordXY const coord_xy(m_mapping(coord_rp)); @@ -167,9 +172,9 @@ class FunctionToBeAdvected_gaussian : public FunctionToBeAdvected double const r = ddc::get(coord_rp); if ((m_rmin <= r) and (r <= m_rmax)) { return m_constant - * std::exp( - -pow(x - m_x0, 2) / (2 * m_sig_x * m_sig_x) - - pow(y - m_y0, 2) / (2 * m_sig_y * m_sig_y)); + * Kokkos::exp( + -ipow(x - m_x0, 2) / (2 * m_sig_x * m_sig_x) + - ipow(y - m_y0, 2) / (2 * m_sig_y * m_sig_y)); } else { return 0.0; } @@ -178,44 +183,9 @@ class FunctionToBeAdvected_gaussian : public FunctionToBeAdvected -// TEST ADVECTION FIELDS ---------------------------------------------------------- -/** - * @brief Base class for the advection field for the tests of the 2D polar advection - * operator. - * - * @see BslAdvectionRTheta - * @see AdvectionSimulation - */ -class AdvectionField -{ -public: - virtual ~AdvectionField() = default; - - /** - * @brief Get the advection field in the physical index range. - * - * @param[in] coord - * The coordinate in the physical index range. - * @param[in] t - * Time component. - * - * @return The advection field in the physical index range. - */ - virtual CoordXY operator()(CoordXY const coord, double const t = 0.) const = 0; - - /** - * @brief Get the characteristic feet in the physical index range. - * - * @param[in] coord - * The original coordinate in the physical index range. - * @param[in] t - * Time component. - * - * @return The characteristic feet in the physical index range. - */ - virtual CoordXY exact_feet(CoordXY coord, double t) const = 0; -}; - +//--------------------------------------------------------------------------------- +// TEST ADVECTION FIELDS +//--------------------------------------------------------------------------------- /** * @brief Advection field for the tests of the 2D polar advection operator. @@ -231,7 +201,7 @@ class AdvectionField * - @f$ Y(t + dt) = y_c + (X(t) - x_c) \sin(\omega dt) + (Y(t) - y_c) \cos(\omega dt) @f$. * */ -class AdvectionField_decentred_rotation : public AdvectionField +class AdvectionField_decentred_rotation { private: double const m_omega; @@ -254,21 +224,46 @@ class AdvectionField_decentred_rotation : public AdvectionField { } - CoordXY operator()(CoordXY const coord, double const t) const final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION AdvectionField_decentred_rotation( + AdvectionField_decentred_rotation const&) + = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION CoordXY operator()(CoordXY const coord, double const t) const { double const x = m_omega * (m_yc - ddc::get(coord)); double const y = m_omega * (ddc::get(coord) - m_xc); return CoordXY(x, y); } - CoordXY exact_feet(CoordXY coord, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord, double const t) const { double const x = ddc::get(coord); double const y = ddc::get(coord); - double const foot_x - = m_xc + (x - m_xc) * std::cos(m_omega * -t) - (y - m_yc) * std::sin(m_omega * -t); - double const foot_y - = m_yc + (x - m_xc) * std::sin(m_omega * -t) + (y - m_yc) * std::cos(m_omega * -t); + double const foot_x = m_xc + (x - m_xc) * Kokkos::cos(m_omega * -t) + - (y - m_yc) * Kokkos::sin(m_omega * -t); + double const foot_y = m_yc + (x - m_xc) * Kokkos::sin(m_omega * -t) + + (y - m_yc) * Kokkos::cos(m_omega * -t); return CoordXY(foot_x, foot_y); } }; @@ -290,7 +285,7 @@ class AdvectionField_decentred_rotation : public AdvectionField * - @f$ Y(t + dt) = Y(t) + dt v_y @f$. * */ -class AdvectionField_translation : public AdvectionField +class AdvectionField_translation { private: CoordXY const m_velocity; @@ -309,12 +304,36 @@ class AdvectionField_translation : public AdvectionField { } - CoordXY operator()(CoordXY const coord, double const t) const final + /// Copy operator + KOKKOS_DEFAULTED_FUNCTION AdvectionField_translation(AdvectionField_translation const&) + = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION CoordXY operator()(CoordXY const coord, double const t) const { return m_velocity; } - CoordXY exact_feet(CoordXY coord, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord, double const t) const { return coord - t * m_velocity; } @@ -341,7 +360,7 @@ class AdvectionField_translation : public AdvectionField * - and @f$ (R(t), \Theta(t)) = \mathcal{F}^{-1} (X(t), Y(t))@f$. * */ -class AdvectionField_rotation : public AdvectionField +class AdvectionField_rotation { private: double const m_vr; @@ -367,7 +386,20 @@ class AdvectionField_rotation : public AdvectionField { } - CoordXY operator()(CoordXY const coord, double const t) const final + /// Copy operator + KOKKOS_DEFAULTED_FUNCTION AdvectionField_rotation(AdvectionField_rotation const&) = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION CoordXY operator()(CoordXY const coord, double const t) const { CoordRTheta const coord_rp(m_physical_to_logical_mapping(coord)); std::array, 2> jacobian; @@ -377,7 +409,17 @@ class AdvectionField_rotation : public AdvectionField return CoordXY(vx, vy); } - CoordXY exact_feet(CoordXY coord_xy, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord_xy + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord_xy, double const t) const { CoordRTheta const coord_rp(m_physical_to_logical_mapping(coord_xy)); CoordRTheta const velocity(m_vr, m_vtheta); @@ -401,57 +443,18 @@ class AdvectionField_rotation : public AdvectionField * @see AdvectionField */ template -class AdvectionSimulation +struct AdvectionSimulation { -protected: /** * @brief The chosen advection field for the simulation. */ - AdvectionField const m_advection_field; + AdvectionField const advection_field; /** * @brief The chosen function to be advected for the simulation. */ - FunctionToBeAdvected const m_function; - -public: - /** - * @brief Instantiate a AdvectionSimulation simulation. - * - * @param[in] advection_field - * An AdvectionField type object. - * @param[in] function - * A FunctionToBeAdvected type object. - */ - AdvectionSimulation(AdvectionField const advection_field, FunctionToBeAdvected const function) - : m_advection_field(advection_field) - , m_function(function) - { - } - virtual ~AdvectionSimulation() = default; - - - /** - * @brief Get the advection field of the simulation. - * - * @return A constant reference to the advection field created in the AdvectionSimulation child class. - */ - AdvectionField const& get_advection_field() const - { - return m_advection_field; - } - - /** - * @brief Get the test function of the simulation. - * - * @return A constant reference to the test function created in the AdvectionSimulation child class. - */ - FunctionToBeAdvected const& get_test_function() const - { - return m_function; - } + FunctionToBeAdvected const advected_function; }; - /** * @brief Simulation of a translated Gaussian. * @@ -475,31 +478,16 @@ class AdvectionSimulation * @see AdvectionField_translation */ template -class TranslationSimulation - : public AdvectionSimulation> +AdvectionSimulation> +get_translation_simulation(Mapping const& mapping, double const rmin, double const rmax) { -public: - /** - * @brief Instantiate a TranslationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - * @param[in] rmin - * The minimum value of @f$ r@f$ on the logical index range. - * @param[in] rmax - * The maximum value of @f$ r@f$ on the logical index range. - */ - TranslationSimulation(Mapping const& mapping, double const rmin, double const rmax) - : AdvectionSimulation>( - AdvectionField_translation( - CoordVx(std::cos(2 * M_PI * 511. / 4096.) / 2.), - CoordVy(std::sin(2 * M_PI * 511. / 4096.) / 2.)), - FunctionToBeAdvected_gaussian< - Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax)) - { - } -}; - + return AdvectionSimulation>( + {AdvectionField_translation( + CoordVx(std::cos(2 * M_PI * 511. / 4096.) / 2.), + CoordVy(std::sin(2 * M_PI * 511. / 4096.) / 2.)), + FunctionToBeAdvected_gaussian< + Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax)}); +} /** * @brief Simulation of a rotated Gaussian. @@ -526,29 +514,14 @@ class TranslationSimulation * @see AdvectionField_rotation */ template -class RotationSimulation - : public AdvectionSimulation> +AdvectionSimulation> +get_rotation_simulation(Mapping const& mapping, double const rmin, double const rmax) { -public: - /** - * @brief Instantiate a RotationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - * @param[in] rmin - * The minimum value of @f$ r@f$ on the logical index range. - * @param[in] rmax - * The maximum value of @f$ r@f$ on the logical index range. - */ - RotationSimulation(Mapping const& mapping, double const rmin, double const rmax) - : AdvectionSimulation>( - AdvectionField_rotation(CoordVr(0.), CoordVtheta(2 * M_PI)), - FunctionToBeAdvected_gaussian< - Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax)) - { - } -}; - + return AdvectionSimulation>( + {AdvectionField_rotation(CoordVr(0.), CoordVtheta(2 * M_PI)), + FunctionToBeAdvected_gaussian< + Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax)}); +} /** * @brief Simulation of a decentred rotated elipse-type function. @@ -576,24 +549,12 @@ class RotationSimulation * @see AdvectionField_decentred_rotation */ template -class DecentredRotationSimulation - : public AdvectionSimulation< - AdvectionField_decentred_rotation, - FunctionToBeAdvected_cos_4_elipse> +AdvectionSimulation> +get_decentred_rotation_simulation(Mapping const& mapping) { -public: - /** - * @brief Instantiate a DecentredRotationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - */ - explicit DecentredRotationSimulation(Mapping const& mapping) - : AdvectionSimulation< - AdvectionField_decentred_rotation, - FunctionToBeAdvected_cos_4_elipse>( - AdvectionField_decentred_rotation(), - FunctionToBeAdvected_cos_4_elipse(mapping)) - { - } -}; + return AdvectionSimulation< + AdvectionField_decentred_rotation, + FunctionToBeAdvected_cos_4_elipse>( + {AdvectionField_decentred_rotation(), + FunctionToBeAdvected_cos_4_elipse(mapping)}); +} diff --git a/tests/geometryRTheta/advection_field_rp/CMakeLists.txt b/tests/geometryRTheta/advection_field_rp/CMakeLists.txt index d77845be7..6c8fd322e 100644 --- a/tests/geometryRTheta/advection_field_rp/CMakeLists.txt +++ b/tests/geometryRTheta/advection_field_rp/CMakeLists.txt @@ -40,7 +40,9 @@ function (advection_field_test_executable SIMULATION) gslx::simulation_utils ) target_compile_definitions("${test_name_gtest}" PUBLIC -D${SIMULATION}) - gtest_discover_tests("${test_name_gtest}" DISCOVERY_MODE PRE_TEST) + gtest_discover_tests("${test_name_gtest}" + TEST_SUFFIX ${SIMULATION} + DISCOVERY_MODE PRE_TEST) endfunction() # Select the parameters of the simulation: diff --git a/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp b/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp index 29137fad6..1bb73906a 100644 --- a/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp +++ b/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp @@ -160,11 +160,14 @@ TEST(AdvectionFieldRThetaComputation, TestAdvectionFieldFinder) // --- Choice of the simulation ------------------------------------------------------------------- #if defined(TRANSLATION) - TranslationAdvectionFieldSimulation simulation(to_physical_mapping, rmin, rmax); + AdvectionFieldSimulation simulation + = get_translation_advection_field_simulation(to_physical_mapping, rmin, rmax); #elif defined(ROTATION) - RotationAdvectionFieldSimulation simulation(to_physical_mapping, rmin, rmax); + AdvectionFieldSimulation simulation + = get_rotation_advection_field_simulation(to_physical_mapping, rmin, rmax); #elif defined(DECENTRED_ROTATION) - DecentredRotationAdvectionFieldSimulation simulation(to_physical_mapping); + AdvectionFieldSimulation simulation + = get_decentred_rotation_advection_field_simulation(to_physical_mapping); #endif // ================================================================================================ @@ -200,18 +203,15 @@ TEST(AdvectionFieldRThetaComputation, TestAdvectionFieldFinder) // Initialize functions ****************************************** - auto function = simulation.get_test_function(); - auto phi_function = simulation.get_electrostatique_potential(); - auto advection_field = simulation.get_advection_field(); ddc::for_each(grid, [&](IdxRTheta const irp) { CoordRTheta const coord_rp(ddc::coordinate(irp)); CoordXY const coord_xy(to_physical_mapping(coord_rp)); - allfdistribu_rp(irp) = function(coord_rp); + allfdistribu_rp(irp) = simulation.function(coord_rp); allfdistribu_xy(irp) = allfdistribu_rp(irp); - electrostatic_potential(irp) = phi_function(coord_xy, 0); + electrostatic_potential(irp) = simulation.electrostatical_potential(coord_xy, 0); - CoordXY const evaluated_advection_field = advection_field(coord_xy, 0); + CoordXY const evaluated_advection_field = simulation.advection_field(coord_xy, 0); ddcHelper::get(advection_field_exact)(irp) = CoordX(evaluated_advection_field); ddcHelper::get(advection_field_exact)(irp) = CoordY(evaluated_advection_field); }); diff --git a/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp b/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp index d80c311e5..90c78af18 100644 --- a/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp +++ b/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp @@ -21,44 +21,6 @@ // TEST ELECTROSTATICAL POTENTIALS ------------------------------------------------ -/** - * @brief Base class for the advection field for the tests of the 2D polar advection - * operator. - * - * @see BslAdvectionRTheta - * @see AdvectionSimulation - */ -class ElectrostaticalPotentialSimulation -{ -public: - virtual ~ElectrostaticalPotentialSimulation() = default; - - /** - * @brief Get the advection field in the physical index range. - * - * @param[in] coord - * The coordinate in the physical index range. - * @param[in] t - * Time component. - * - * @return The advection field in the physical index range. - */ - virtual double operator()(CoordXY const coord, double const t = 0.) const = 0; - - /** - * @brief Get the characteristic feet in the physical index range. - * - * @param[in] coord - * The original coordinate in the physical index range. - * @param[in] t - * Time component. - * - * @return The characteristic feet in the physical index range. - */ - virtual CoordXY exact_feet(CoordXY coord, double t) const = 0; -}; - - /** * @brief Advection field for the tests of the 2D polar advection operator. * @@ -74,7 +36,6 @@ class ElectrostaticalPotentialSimulation * */ class ElectrostaticalPotentialSimulation_decentred_rotation - : public ElectrostaticalPotentialSimulation { private: double const m_omega; @@ -88,7 +49,7 @@ class ElectrostaticalPotentialSimulation_decentred_rotation * @brief Instantiate an ElectrostaticalPotentialSimulation_decentred_rotation advection field. * */ - ElectrostaticalPotentialSimulation_decentred_rotation() + KOKKOS_FUNCTION ElectrostaticalPotentialSimulation_decentred_rotation() : m_omega(2 * M_PI) , m_xc(0.25) , m_yc(0.) @@ -97,21 +58,46 @@ class ElectrostaticalPotentialSimulation_decentred_rotation { } - double operator()(CoordXY const coord, double const t) const final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION ElectrostaticalPotentialSimulation_decentred_rotation( + ElectrostaticalPotentialSimulation_decentred_rotation const&) + = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION double operator()(CoordXY const coord, double const t) const { double const x = ddc::get(coord); double const y = ddc::get(coord); return m_omega * (-0.5 * x * x + m_xc * x - 0.5 * y * y + m_yc * y); } - CoordXY exact_feet(CoordXY coord, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord, double const t) const { double const x = ddc::get(coord); double const y = ddc::get(coord); - double const foot_x - = m_xc + (x - m_xc) * std::cos(m_omega * -t) - (y - m_yc) * std::sin(m_omega * -t); - double const foot_y - = m_yc + (x - m_xc) * std::sin(m_omega * -t) + (y - m_yc) * std::cos(m_omega * -t); + double const foot_x = m_xc + (x - m_xc) * Kokkos::cos(m_omega * -t) + - (y - m_yc) * Kokkos::sin(m_omega * -t); + double const foot_y = m_yc + (x - m_xc) * Kokkos::sin(m_omega * -t) + + (y - m_yc) * Kokkos::cos(m_omega * -t); return CoordXY(foot_x, foot_y); } }; @@ -133,7 +119,7 @@ class ElectrostaticalPotentialSimulation_decentred_rotation * - @f$ Y(t + dt) = Y(t) + dt v_y @f$. * */ -class ElectrostaticalPotentialSimulation_translation : public ElectrostaticalPotentialSimulation +class ElectrostaticalPotentialSimulation_translation { private: CoordXY const m_velocity; @@ -152,14 +138,39 @@ class ElectrostaticalPotentialSimulation_translation : public ElectrostaticalPot { } - double operator()(CoordXY const coord, double const t) const final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION ElectrostaticalPotentialSimulation_translation( + ElectrostaticalPotentialSimulation_translation const&) + = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION double operator()(CoordXY const coord, double const t) const { double const vx = ddc::get(m_velocity); double const vy = ddc::get(m_velocity); return -vy * ddc::get(coord) + vx * ddc::get(coord); } - CoordXY exact_feet(CoordXY coord, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord, double const t) const { return coord - t * m_velocity; } @@ -186,7 +197,7 @@ class ElectrostaticalPotentialSimulation_translation : public ElectrostaticalPot * - and @f$ (R(t), \Theta(t)) = \mathcal{F}^{-1} (X(t), Y(t))@f$. * */ -class ElectrostaticalPotentialSimulation_rotation : public ElectrostaticalPotentialSimulation +class ElectrostaticalPotentialSimulation_rotation { private: double const m_vr; @@ -207,14 +218,39 @@ class ElectrostaticalPotentialSimulation_rotation : public ElectrostaticalPotent { } - double operator()(CoordXY const coord, double const t) const final + /// Copy operator + explicit KOKKOS_DEFAULTED_FUNCTION ElectrostaticalPotentialSimulation_rotation( + ElectrostaticalPotentialSimulation_rotation const&) + = default; + + /** + * @brief Get the advection field in the physical index range. + * + * @param[in] coord + * The coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The advection field in the physical index range. + */ + KOKKOS_FUNCTION double operator()(CoordXY const coord, double const t) const { CoordRTheta const coord_rp(m_mapping(coord)); double const r = ddc::get(coord_rp); return -0.5 * r * r * m_vtheta; } - CoordXY exact_feet(CoordXY coord_xy, double const t) const final + /** + * @brief Get the characteristic feet in the physical index range. + * + * @param[in] coord_xy + * The original coordinate in the physical index range. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical index range. + */ + KOKKOS_FUNCTION CoordXY exact_feet(CoordXY coord_xy, double const t) const { CoordRTheta const coord_rp(m_mapping(coord_xy)); CoordRTheta const velocity(m_vr, m_vtheta); @@ -243,77 +279,20 @@ template < class ElectrostaticalPotentialSimulation, class FunctionToBeAdvected, class AdvectionField> -class AdvectionFieldSimulation +struct AdvectionFieldSimulation { -protected: /** * @brief The chosen electrostatical potential for the simulation. */ - ElectrostaticalPotentialSimulation const m_electrostatical_potential; + ElectrostaticalPotentialSimulation const electrostatical_potential; /** * @brief The chosen function to be advected for the simulation. */ - FunctionToBeAdvected const m_function; + FunctionToBeAdvected const function; /** * @brief The chosen advection field for the simulation. */ - AdvectionField const m_advection_field; - - -public: - /** - * @brief Instantiate a AdvectionSimulation simulation. - * - * @param[in] electrostatical_potential - * An ElectrostaticalPotentialSimulation type object. - * @param[in] function - * A FunctionToBeAdvected type object. - * @param[in] advection_field - * A AdvectionField type object. - */ - AdvectionFieldSimulation( - ElectrostaticalPotentialSimulation const electrostatical_potential, - FunctionToBeAdvected const function, - AdvectionField const advection_field) - : m_electrostatical_potential(electrostatical_potential) - , m_function(function) - , m_advection_field(advection_field) - { - } - virtual ~AdvectionFieldSimulation() = default; - - - /** - * @brief Get the electrostatical potential of the simulation. - * - * @return A constant reference to the electrostatical potential created in - * the ElectrostaticalPotentialSimulation child class. - */ - ElectrostaticalPotentialSimulation const& get_electrostatique_potential() const - { - return m_electrostatical_potential; - } - - /** - * @brief Get the test function of the simulation. - * - * @return A constant reference to the test function created in the AdvectionSimulation child class. - */ - FunctionToBeAdvected const& get_test_function() const - { - return m_function; - } - - - /** - * @brief Get the advection field of the simulation. - * - * @return A constant reference to the advection field created in the AdvectionSimulation child class. - */ - AdvectionField const& get_advection_field() const - { - return m_advection_field; - } + AdvectionField const advection_field; }; @@ -336,47 +315,39 @@ class AdvectionFieldSimulation * * with @f$ v_x @f$ and @f$ v_y @f$ constants. * + * @param[in] mapping + * The mapping from the logical index range to the physical index range. + * @param[in] rmin + * The minimum value of @f$ r@f$ on the logical index range. + * @param[in] rmax + * The maximum value of @f$ r@f$ on the logical index range. + * * @see FunctionToBeAdvected_gaussian * @see ElectrostaticalPotentialSimulation_translation * @see AdvectionField_translation */ template -class TranslationAdvectionFieldSimulation - : public AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_translation, - FunctionToBeAdvected_gaussian, - AdvectionField_translation> +AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_translation, + FunctionToBeAdvected_gaussian, + AdvectionField_translation> +get_translation_advection_field_simulation( + Mapping const& mapping, + double const rmin, + double const rmax) { -public: - /** - * @brief Instantiate a TranslationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - * @param[in] rmin - * The minimum value of @f$ r@f$ on the logical index range. - * @param[in] rmax - * The maximum value of @f$ r@f$ on the logical index range. - */ - TranslationAdvectionFieldSimulation( - Mapping const& mapping, - double const rmin, - double const rmax) - : AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_translation, - FunctionToBeAdvected_gaussian, - AdvectionField_translation>( - ElectrostaticalPotentialSimulation_translation( - CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), - CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.)), - FunctionToBeAdvected_gaussian< - Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), - AdvectionField_translation( - CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), - CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.))) - { - } -}; + return AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_translation, + FunctionToBeAdvected_gaussian, + AdvectionField_translation>( + {ElectrostaticalPotentialSimulation_translation( + CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), + CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.)), + FunctionToBeAdvected_gaussian(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), + AdvectionField_translation( + CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), + CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.))}); +} /** @@ -400,40 +371,35 @@ class TranslationAdvectionFieldSimulation * with @f$ v_r @f$ and @f$ v_\theta @f$ constants and @f$ \mathcal{F}@f$ the * circular mapping. * + * @param[in] mapping + * The mapping from the logical index range to the physical index range. + * @param[in] rmin + * The minimum value of @f$ r@f$ on the logical index range. + * @param[in] rmax + * The maximum value of @f$ r@f$ on the logical index range. + * * @see FunctionToBeAdvected_gaussian * @see ElectrostaticalPotentialSimulation_rotation * @see AdvectionField_rotation */ template -class RotationAdvectionFieldSimulation - : public AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_rotation, - FunctionToBeAdvected_gaussian, - AdvectionField_rotation> +AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_rotation, + FunctionToBeAdvected_gaussian, + AdvectionField_rotation> +get_rotation_advection_field_simulation( + Mapping const& mapping, + double const rmin, + double const rmax) { -public: - /** - * @brief Instantiate a RotationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - * @param[in] rmin - * The minimum value of @f$ r@f$ on the logical index range. - * @param[in] rmax - * The maximum value of @f$ r@f$ on the logical index range. - */ - RotationAdvectionFieldSimulation(Mapping const& mapping, double const rmin, double const rmax) - : AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_rotation, - FunctionToBeAdvected_gaussian, - AdvectionField_rotation>( - ElectrostaticalPotentialSimulation_rotation(CoordVtheta(2 * M_PI)), - FunctionToBeAdvected_gaussian< - Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), - AdvectionField_rotation(CoordVr(0), CoordVtheta(2 * M_PI))) - { - } -}; + return AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_rotation, + FunctionToBeAdvected_gaussian, + AdvectionField_rotation>( + {ElectrostaticalPotentialSimulation_rotation(CoordVtheta(2 * M_PI)), + FunctionToBeAdvected_gaussian(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), + AdvectionField_rotation(CoordVr(0), CoordVtheta(2 * M_PI))}); +} /** @@ -457,33 +423,25 @@ class RotationAdvectionFieldSimulation * * with @f$\omega = 2\pi@f$ and @f$ (x_c, y_c) = (0.25, 0) @f$. * + * @param[in] mapping + * The mapping from the logical index range to the physical index range. * * @see FunctionToBeAdvected_cos_4_elipse * @see ElectrostaticalPotentialSimulation_decentred_rotation * @see AdvectionField_decentred_rotation */ template -class DecentredRotationAdvectionFieldSimulation - : public AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_decentred_rotation, - FunctionToBeAdvected_cos_4_elipse, - AdvectionField_decentred_rotation> +AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_decentred_rotation, + FunctionToBeAdvected_cos_4_elipse, + AdvectionField_decentred_rotation> +get_decentred_rotation_advection_field_simulation(Mapping const& mapping) { -public: - /** - * @brief Instantiate a DecentredRotationSimulation simulation. - * - * @param[in] mapping - * The mapping from the logical index range to the physical index range. - */ - explicit DecentredRotationAdvectionFieldSimulation(Mapping const& mapping) - : AdvectionFieldSimulation< - ElectrostaticalPotentialSimulation_decentred_rotation, - FunctionToBeAdvected_cos_4_elipse, - AdvectionField_decentred_rotation>( - ElectrostaticalPotentialSimulation_decentred_rotation(), - FunctionToBeAdvected_cos_4_elipse(mapping), - AdvectionField_decentred_rotation()) - { - } -}; + return AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_decentred_rotation, + FunctionToBeAdvected_cos_4_elipse, + AdvectionField_decentred_rotation>( + {ElectrostaticalPotentialSimulation_decentred_rotation(), + FunctionToBeAdvected_cos_4_elipse(mapping), + AdvectionField_decentred_rotation()}); +}