43 int find_strict_lower_bound(
const Eigen::VectorXd& grid,
double value)
45 if (value < grid(0)) {
47 }
else if (value > grid(grid.size() - 1)) {
48 return grid.size() - 1;
52 grid.unaryExpr([value](
double x) {
53 return Abs(x - value); }).minCoeff(&closest);
55 return grid(closest) < value ? closest : closest - 1;
70 int find_strict_upper_bound(
const Eigen::VectorXd& grid,
double value)
72 if (value < grid(0)) {
74 }
else if (value > grid(grid.size() - 1)) {
79 grid.unaryExpr([value](
double x) {
80 return Abs(x - value); }).minCoeff(&closest);
82 return grid(closest) > value ? closest : closest + 1;
85 Eigen::VectorXd extend_domain(
const Eigen::VectorXd& grid,
double new_start,
86 double new_end,
int new_length)
88 const int old_length = grid.size();
90 if (old_length >= new_length) {
91 throw Setup_error(
"old grid must be shorter than new grid");
94 const double old_start = grid(0);
95 const double old_end = grid(old_length - 1);
97 if (new_start > old_start || new_end < old_end) {
98 throw Setup_error(
"new domain boundaries cannot be within old grid");
101 const double extra_length = old_start - new_start + new_end - old_end;
102 const double ratio = (old_start - new_start) / extra_length;
103 const int n_extra = new_length - old_length;
104 const int n_front =
static_cast<int>(std::floor(ratio * n_extra));
105 const int n_back = n_extra - n_front;
107 Eigen::VectorXd new_grid(new_length);
108 new_grid.segment(n_front, old_length) = grid;
111 const double front_step = (old_start - new_start) / n_front;
112 for (
int i = 0; i < n_front; ++i) {
113 new_grid(i) = new_start + i * front_step;
118 const double back_step = (new_end - old_end) / n_back;
119 for (
int i = 0; i < n_back; ++i) {
120 new_grid(n_front + old_length + i) = old_end + i * back_step;
127 template <
typename InputIt>
128 auto get_subset(
int num, InputIt first, InputIt last)
129 -> std::vector<typename std::iterator_traits<InputIt>::value_type>
131 using value_type =
typename std::iterator_traits<InputIt>::value_type;
134 const auto halfway = std::distance(first, last) / 2;
135 return std::vector<value_type>{ *(first + halfway) };
136 }
else if (num == 2) {
137 return std::vector<value_type>{ *first, *(last - 1) };
140 const auto length = std::distance(first, last);
141 const auto midpoint = length / 2;
143 std::vector<value_type> subset;
147 const auto next_length = num / 2;
149 = get_subset(next_length, first, first + midpoint);
151 = get_subset(next_length, first + midpoint, last);
152 subset.insert(std::end(subset), std::begin(front), std::end(front));
153 subset.insert(std::end(subset), std::begin(back), std::end(back));
155 const auto next_length = (num - 1) / 2;
157 = get_subset(next_length, first, first + midpoint);
159 = get_subset(next_length, first + midpoint + 1, last);
161 subset.insert(std::end(subset), std::begin(front), std::end(front));
162 subset.push_back(*(first + midpoint));
163 subset.insert(std::end(subset), std::begin(back), std::end(back));
169 Eigen::VectorXd injective_coarsening(
const Eigen::VectorXd& grid,
172 const int old_length = grid.size();
174 Eigen::VectorXd new_grid(new_length);
175 if (old_length % new_length == 0) {
176 const int stride = old_length / new_length;
177 for (
int i = 0, j = 0; i < new_length; ++i, j += stride) {
178 new_grid(i) = grid(j);
181 std::vector<int> grid_indices(old_length);
182 std::iota(std::begin(grid_indices), std::end(grid_indices), 0);
183 const auto subset_indices = get_subset(new_length,
184 std::begin(grid_indices),
185 std::end(grid_indices));
186 for (
int i = 0; i < new_length; ++i) {
187 new_grid(i) = grid(subset_indices[i]);
194 Eigen::VectorXd coarsen_grid(
const Eigen::VectorXd& grid,
int new_length)
196 const int old_length = grid.size();
198 if (old_length < new_length) {
199 throw Setup_error(
"old grid must be finer than new grid");
202 return injective_coarsening(grid, new_length);
208 const Potential& potential,
const Eigen::VectorXd& true_vacuum,
int d,
209 double domain_start,
double domain_end,
double initial_step_size,
210 double interpolation_points_fraction)
216 interpolation_points_fraction);
220 const Potential& potential,
const Eigen::VectorXd& true_vacuum)
225 throw Setup_error(
"Shooting_profile_guesser::get_profile_guess: " 226 "True and false vacua are coincident");
230 Eigen::VectorXd origin(Eigen::VectorXd::Zero(
num_fields));
231 if (
Abs(potential(true_vacuum) - potential(origin)) < 1.e-12) {
232 throw Setup_error(
"Shooting_profile_guesser::get_profile_guess: " 233 "True and false vacua are degenerate");
238 const Potential& potential,
const Eigen::VectorXd& true_vacuum)
241 auto ansatz_potential = std::unique_ptr<Potential>(potential.
clone());
247 std::stringstream log_str;
257 ansatz_potential->add_constant_term(-1*ansatz_potential->operator()(
261 Eigen::VectorXd _true_vacuum = Eigen::VectorXd::Zero(
num_fields);
268 Eigen::VectorXd trial_vec = Eigen::VectorXd::Zero(
num_fields);
271 double vtrial = ansatz_potential->operator()(trial_vec);
272 double vmin = ansatz_potential->operator()(_true_vacuum);
273 double r = vmin / vtrial;
277 / ((trial_dist - 1.) * (trial_dist - 1.));
278 alpha = (r * trial_dist * trial_dist * (trial_dist - 1.5) + 0.5)
279 / (1. - r * trial_dist * trial_dist * (2. - trial_dist *
trial_dist));
282 + std::to_string(
alpha));
284 + std::to_string(
Abs(
E)));
289 const auto potential = [
this](
double phi) {
291 -this->
alpha * phi * phi * phi * phi
293 + 0.5 * (4. * this->
alpha - 3.) * phi * phi);
296 const auto potential_first = [
this](
double phi) {
297 return this->
E * (-4. * this->
alpha * phi * phi * phi
299 + (4. * this->
alpha - 3.) * phi);
302 const auto potential_second = [
this](
double phi) {
303 return this->
E * (-12. * this->
alpha * phi * phi
304 + 6. * phi + (4. * this->
alpha - 3.));
311 solver.
solve(potential, potential_first, potential_second,
312 false_min, true_min, barrier, d,
313 Shooting::Solver_options::Compute_profile);
317 int d,
double domain_start,
double domain_end,
318 double initial_step_size,
double interpolation_points_fraction)
327 int num_grid_points = 0;
328 if (domain_start < 0. && domain_end < 0.) {
329 num_grid_points = 1 +
static_cast<int>(
330 std::ceil((solution_end - solution_start) / initial_step_size));
331 if (num_grid_points == n_solution_points) {
334 interpolation_points_fraction);
335 return initial_profile;
338 num_grid_points = 1 +
static_cast<int>(
339 std::ceil((domain_end - domain_start) / initial_step_size));
343 const Eigen::VectorXd rho
345 domain_end, num_grid_points);
347 Eigen::VectorXd ansatz(num_grid_points);
348 for (
int i = 0; i < num_grid_points; ++i) {
349 const double r = rho(i);
350 if (r < solution_start) {
352 }
else if (r >= solution_start && r <= solution_end) {
360 Eigen::MatrixXd cob_reverse =
cob_matrix.transpose();
366 Eigen::MatrixXd m_profiles(num_grid_points,
num_fields);
368 Eigen::VectorXd temp_field_vec = Eigen::VectorXd::Zero(
num_fields);
370 for (
int x = 0; x < num_grid_points; ++x) {
372 temp_field_vec(0) = ansatz(x);
376 Eigen::VectorXd orig_field_vec
379 m_profiles.row(x) = orig_field_vec;
382 Field_profiles profiles(rho, m_profiles, interpolation_points_fraction);
389 const Eigen::VectorXd& old_grid,
double new_start,
double new_end,
390 int new_length)
const 392 const int old_length = old_grid.size();
393 const double old_start = old_grid(0);
394 const double old_end = old_grid(old_length - 1);
396 double grid_start = new_start >= 0. ? new_start : old_start;
397 double grid_end = new_end >= 0. ? new_end : old_end;
399 if (grid_start > grid_end) {
400 std::swap(grid_start, grid_end);
403 const int start_index = find_strict_upper_bound(old_grid, grid_start);
404 const int end_index = find_strict_lower_bound(old_grid, grid_end);
405 if (start_index < 0 || end_index < 0) {
409 return Eigen::VectorXd::LinSpaced(new_length, grid_start, grid_end);
412 Eigen::VectorXd new_grid(new_length);
413 new_grid(0) = grid_start;
414 new_grid(new_length - 1) = grid_end;
416 const int n_interior_points = end_index - start_index + 1;
417 const Eigen::VectorXd interior_points =
418 old_grid.segment(start_index, n_interior_points);
420 if (n_interior_points == new_length - 2) {
421 new_grid.segment(1, new_length - 2) = interior_points;
422 }
else if (n_interior_points < new_length - 2) {
423 new_grid.segment(1, new_length - 2) =
424 extend_domain(interior_points, grid_start, grid_end, new_length - 2);
426 new_grid.segment(1, new_length - 2) =
427 coarsen_grid(interior_points, new_length - 2);
435 double local_min_loc;
439 if (
alpha <= 0.375) {
442 local_min_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
450 local_min_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
456 local_min_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
459 return local_min_loc;
464 double local_max_loc;
471 local_max_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
477 local_max_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
480 if (
alpha <= 0.375) {
485 local_max_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
490 return local_max_loc;
495 double global_min_loc;
500 global_min_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
503 throw Error(
"Shooting_profile_guesser::get_global_minimum_location: " 504 "global minimum does not exist");
509 throw Error(
"Shooting_profile_guesser::get_global_minimum_location: " 510 "global minimum does not exist");
512 global_min_loc = 0.25 * (3. - 4. *
alpha) /
alpha;
519 return global_min_loc;
525 const auto potential_second = [
this](
double phi) {
526 return this->
E * (-12. * this->
alpha * phi * phi
527 + 6. * phi + (4. * this->
alpha - 3.));
531 const double mass_squared = potential_second(local_min);
534 const double y = rho *
Sqrt(
Abs(mass_squared));
535 const double y_max = rho_max *
Sqrt(
Abs(mass_squared));
536 const double phip_max = profile.
derivative_at(0, 1, rho_max);
537 double correction = 0;
538 if (mass_squared > 0) {
540 correction = -phip_max * rho_max * rho_max *
Exp(y_max - y) /
541 (rho * (1. + y_max));
543 correction = -phip_max * rho_max * rho_max *
BesselK(1, y) /
544 (rho * (
BesselK(1, y_max) + 0.5 * y_max * (
549 throw Error(
"Shooting_profile_guesser::get_large_distance_solution: " 550 "cannot evaluate asymptotic solution for this potential");
553 return local_min + correction;
double get_domain_end() const
double calculate_false_min_location() const
double calculate_barrier_location() const
auto BesselK(Order v, Argument z) -> decltype(boost::math::cyl_bessel_k(v, z))
double derivative_at(int field, int order, double rho) const
Take the radial derivative of a field at a given radius.
const Eigen::VectorXd & get_spatial_grid() const
Get a vector of the grid point coordinates.
void set_interpolation_points_fraction(double f)
void solve_one_dimensional(int)
Field_profiles calculate_field_profiles(int, double, double, double, double)
void solve(const std::function< double(double)> &potential_, const std::function< double(double)> &potential_first_, const std::function< double(double)> &potential_second_, double false_min_, double true_min_, double barrier_, int dim_=3, unsigned int options=(Solver_options::Compute_action|Solver_options::Compute_profile))
double get_domain_start() const
void log_message(Log_level level, const std::string &msg) const
const Field_profiles & get_bubble_profile() const
void set_number_of_dimensions(int d)
int get_number_of_grid_points() const
Eigen::MatrixXd cob_matrix
double quadratic_potential_solution(double) const
double calculate_true_min_location() const
Eigen::VectorXd evaluate_at(double rho) const
Get all field values at a given radius.
Eigen::VectorXd update_spatial_grid(const Eigen::VectorXd &, double, double, int) const
virtual Potential * clone() const =0
Subclasses must implement a clone method.
double get_large_distance_solution(double, const Field_profiles &, int) const
logging::Basic_logger logger
virtual Field_profiles get_profile_guess(const Potential &, const Eigen::VectorXd &, int, double, double, double, double) override
Calculate an initial guess for the bubble profile.
Eigen::MatrixXd calculate_rotation_to_target(const Eigen::VectorXd &target)
Calculate the rotation matrix for a rotation to the target vector.
virtual std::size_t get_number_of_fields() const =0
Exception indicating general setup error.
void compute_vacuum_distance(const Potential &, const Eigen::VectorXd &)
Abstract base class for a generic potential.
void calculate_potential_parameters(const Potential &, const Eigen::VectorXd &)
Discretized set of field profiles.