109 const std::string& filename,
110 std::vector<std::array<double, dim>>& normals,
111 std::vector<double>& offsets)
113 std::ifstream in(filename);
115 throw std::runtime_error(
"Cannot open normals file: " + filename);
118 std::size_t file_dim = 0;
119 std::size_t num_facets = 0;
121 in >> file_dim >> num_facets;
123 throw std::runtime_error(
"Error reading header (dim, num_facets) from: " + filename);
126 if (file_dim !=
dim + 1) {
127 throw std::runtime_error(
128 "Dimension mismatch in normals file: file has dim = " +
129 std::to_string(file_dim) +
" but template expects dim = " +
130 std::to_string(
dim));
135 normals.reserve(num_facets);
136 offsets.reserve(num_facets);
138 for (std::size_t f = 0; f < num_facets; ++f) {
139 std::array<double, dim> n{};
142 for (std::size_t k = 0; k <
dim; ++k) {
144 throw std::runtime_error(
145 "Error reading normal component " + std::to_string(k) +
146 " for facet " + std::to_string(f) +
147 " from: " + filename);
152 throw std::runtime_error(
153 "Error reading offset d for facet " + std::to_string(f) +
154 " from: " + filename);
158 normals.push_back(n);
159 offsets.push_back(b);
336 double hole_x = params[0];
337 double hole_y = params[1];
338 double hole_z = params[2];
339 double hole_r = params[3];
344 hole_center[0] = hole_x;
345 hole_center[1] = hole_y;
346 hole_center[2] = hole_z;
347 if (!domain.
isInside(hole_center)) {
353 std::mt19937 rng(local_seed);
356 std::uniform_real_distribution<double> dist_x(bounds[0].first, bounds[0].second);
357 std::uniform_real_distribution<double> dist_y(bounds[1].first, bounds[1].second);
358 std::uniform_real_distribution<double> dist_z(bounds[2].first, bounds[2].second);
360 int n_samples = 20000;
361 double hole_r_sq = hole_r * hole_r;
363 double sum_mass = 0.0;
369 for (
int i = 0; i < n_samples; ++i) {
376 double dist_sq = std::pow(p[0] - hole_x, 2) +
377 std::pow(p[1] - hole_y, 2) +
378 std::pow(p[2] - hole_z, 2);
379 if (dist_sq > hole_r_sq) {
388 if (sum_mass <= 1e-9)
return 1e6;
390 double cm_x = sum_mx / sum_mass;
391 double cm_y = sum_my / sum_mass;
392 double cm_z = sum_mz / sum_mass;
394 double error = std::sqrt(std::pow(cm_x - 1.0, 2) +
395 std::pow(cm_y - 0.0, 2) +
396 std::pow(cm_z - 0.0, 2));
404int main(
int argc,
char* argv[]) {
406 int num_threads = omp_get_max_threads();
407 std::uint32_t seed = 12345;
410 std::string seed_arg = argv[1];
412 if (seed_arg ==
"-") {
413 std::cout <<
"Using default seed: " << seed << std::endl;
416 seed =
static_cast<std::uint32_t
>(std::stoul(seed_arg));
417 std::cout <<
"Using custom seed: " << seed << std::endl;
418 }
catch (
const std::exception&) {
419 std::cerr <<
"Invalid seed argument. Using default seed: " << seed << std::endl;
423 std::cout <<
"Using default seed: " << seed << std::endl;
432 int requested_threads = std::stoi(argv[2]);
433 if (requested_threads == 0) {
435 omp_set_num_threads(1);
436 std::cout <<
"Running SEQUENTIAL (1 thread)" << std::endl;
437 }
else if (requested_threads > 0) {
438 num_threads = requested_threads;
439 omp_set_num_threads(requested_threads);
440 std::cout <<
"Running with " << num_threads <<
" threads" << std::endl;
442 }
catch (
const std::exception&) {
443 std::cout <<
"Invalid thread argument. Using max available: " << num_threads << std::endl;
446 std::cout <<
"Using max available threads: " << num_threads << std::endl;
447 std::cout <<
"(Usage: ./drone_optimization [seed|-] [num_threads])" << std::endl;
448 std::cout <<
" <num_threads>: 0=sequential, N>0=N threads" << std::endl;
450 std::cout << std::endl;
452 std::cout <<
"--- Drone Arm Center of Mass Optimization ---" << std::endl;
453 std::cout <<
"Optimizing hole position and size to shift CM to (1.0, 0, 0)..." << std::endl;
454 std::cout << std::endl;
459 std::cout <<
"--- Pre-computing Static Drone Properties (Baseline) ---" << std::endl;
465 int n_baseline = 1000000;
473 double base_mass = static_integrator.
OLDintegrate(f_mass, n_baseline);
474 double base_mx = static_integrator.
OLDintegrate(f_mx, n_baseline);
475 double base_my = static_integrator.
OLDintegrate(f_my, n_baseline);
476 double base_mz = static_integrator.
OLDintegrate(f_mz, n_baseline);
478 std::cout << std::fixed << std::setprecision(8);
479 std::cout <<
"Baseline Mass (Full Body): " << base_mass << std::endl;
480 std::cout <<
"Baseline CM: (" << base_mx/base_mass <<
", "
481 << base_my/base_mass <<
", "
482 << base_mz/base_mass <<
")" << std::endl;
483 std::cout << std::endl;
499 std::vector<double> lower_bounds = {-4.0, -1.0, -0.5, 0.1};
500 std::vector<double> upper_bounds = { 4.0, 1.0, 0.5, 0.9};
502 pso.
setBounds(lower_bounds, upper_bounds);
507 if (iter % 10 == 0) {
508 std::cout <<
"Iter " << iter <<
" | Error: " << best.
value <<
" | ";
509 std::cout <<
"Hole: x=" << best.
params[0] <<
" r=" << best.
params[3] << std::endl;
516 std::cout <<
"\n--- Optimization Complete ---" << std::endl;
517 std::cout <<
"Final Error: " << best_sol.
value << std::endl;
518 std::cout <<
"Optimal Hole Configuration:" << std::endl;
519 std::cout <<
" Position: (" << best_sol.
params[0] <<
", "
520 << best_sol.
params[1] <<
", "
521 << best_sol.
params[2] <<
")" << std::endl;
522 std::cout <<
" Radius: " << best_sol.
params[3] << std::endl;
525 double best_x = best_sol.
params[0];
526 double best_y = best_sol.
params[1];
527 double best_z = best_sol.
params[2];
528 double best_r = best_sol.
params[3];
533 std::cout <<
"\n--- HIGH-PRECISION VERIFICATION ---" << std::endl;
538 int n_verify = 1000000;
539 double best_r_sq = best_r * best_r;
541 long double total_hits = 0.0;
542 long double total_mx = 0.0;
543 long double total_my = 0.0;
544 long double total_mz = 0.0;
546 #pragma omp parallel reduction(+:total_hits, total_mx, total_my, total_mz)
548 int tid = omp_get_thread_num();
553 for (
int i = 0; i < n_verify; ++i) {
555 double rx = std::generate_canonical<double, 32>(local_rng);
556 double ry = std::generate_canonical<double, 32>(local_rng);
557 double rz = std::generate_canonical<double, 32>(local_rng);
558 p[0] = bounds[0].first + rx * (bounds[0].second - bounds[0].first);
559 p[1] = bounds[1].first + ry * (bounds[1].second - bounds[1].first);
560 p[2] = bounds[2].first + rz * (bounds[2].second - bounds[2].first);
563 double dist_sq = std::pow(p[0] - best_x, 2) +
564 std::pow(p[1] - best_y, 2) +
565 std::pow(p[2] - best_z, 2);
566 if (dist_sq > best_r_sq) {
576 if (total_hits <= 0.0) {
577 std::cout <<
"No mass remains after hole removal; verification aborting." << std::endl;
582 double mass_high_prec = (
static_cast<double>(total_hits) / n_verify) * box_vol;
584 double cm_x_final =
static_cast<double>(total_mx / total_hits);
585 double cm_y_final =
static_cast<double>(total_my / total_hits);
586 double cm_z_final =
static_cast<double>(total_mz / total_hits);
589 double error_final = std::sqrt(std::pow(cm_x_final - CM_target_verify[0], 2) +
590 std::pow(cm_y_final - CM_target_verify[1], 2) +
591 std::pow(cm_z_final - CM_target_verify[2], 2));
593 std::cout << std::fixed << std::setprecision(8);
594 std::cout <<
"\nResults with " << n_verify <<
" samples (high-precision verification):" << std::endl;
595 std::cout <<
"Total mass estimated: " << mass_high_prec << std::endl;
596 std::cout <<
"Current center of mass: (" << cm_x_final <<
", "
597 << cm_y_final <<
", "
598 << cm_z_final <<
")" << std::endl;
599 std::cout <<
"Target center of mass: (" << CM_target_verify[0] <<
", "
600 << CM_target_verify[1] <<
", "
601 << CM_target_verify[2] <<
")" << std::endl;
602 std::cout <<
"Residual error (verified):" << error_final << std::endl;
603 std::cout <<
"\nComparison:" << std::endl;
604 std::cout <<
" Optimization error (few samples): " << best_sol.
value << std::endl;
605 std::cout <<
" Verification error (many samples): " << error_final << std::endl;
610 std::cout <<
"\n--- HYBRID VERIFICATION (Baseline MC + Analytic Hole) ---" << std::endl;
613 const double pi = std::acos(-1.0);
614 double vol_hole = (4.0 / 3.0) * pi * std::pow(best_r, 3);
615 double mom_x_hole = vol_hole * best_x;
616 double mom_y_hole = vol_hole * best_y;
617 double mom_z_hole = vol_hole * best_z;
620 double final_mass_exact = base_mass - vol_hole;
621 double final_mx_exact = base_mx - mom_x_hole;
622 double final_my_exact = base_my - mom_y_hole;
623 double final_mz_exact = base_mz - mom_z_hole;
625 if (final_mass_exact <= 0.0) {
626 std::cout <<
"Invalid: hole removes all mass (should not happen with valid PSO solution)" << std::endl;
628 double true_cm_x = final_mx_exact / final_mass_exact;
629 double true_cm_y = final_my_exact / final_mass_exact;
630 double true_cm_z = final_mz_exact / final_mass_exact;
632 double exact_error = std::sqrt(std::pow(true_cm_x - 1.0, 2) +
633 std::pow(true_cm_y - 0.0, 2) +
634 std::pow(true_cm_z - 0.0, 2));
636 std::cout <<
"Hybrid Mass (Baseline - Hole): " << final_mass_exact << std::endl;
637 std::cout <<
"Hybrid CM (Ground Truth): (" << true_cm_x <<
", "
639 << true_cm_z <<
")" << std::endl;
640 std::cout <<
"Hybrid Error from Target: " << exact_error << std::endl;
642 double method_diff = std::abs(error_final - exact_error);
643 std::cout <<
"Delta (MC Verify vs Hybrid): " << method_diff << std::endl;
645 if (method_diff < 0.05) {
646 std::cout <<
"\n✓ SUCCESS: Monte Carlo converges to physical ground truth." << std::endl;
648 std::cout <<
"\n⚠ WARNING: Residual discrepancy detected (consider increasing n_baseline or n_verify)." << std::endl;
652 if (error_final < 0.01) {
653 std::cout <<
"\n✓ ROBUST solution: error remains small even with many samples!" << std::endl;
654 }
else if (error_final < 0.1) {
655 std::cout <<
"\n◎ MODERATE solution: could improve with more PSO iterations." << std::endl;
657 std::cout <<
"\n✗ UNSTABLE solution: consider increasing PSO population/iterations." << std::endl;
661 std::cout <<
"\nExporting domain geometry to drone_frames/" << std::endl;
662 domain_verify.
exportGeometry(
"./drone_frames", best_x, best_y, best_z, best_r);
void exportGeometry(const std::string &output_dir, double hx, double hy, double hz, double hr) const
std::unique_ptr< mc::domains::PolyTope< DIM > > cabin
double getBoxVolume() const override
Compute the volume of the bounding box.
bool isInside(const mc::geom::Point< DIM > &p) const override
std::unique_ptr< mc::domains::HyperRectangle< DIM > > arm
std::unique_ptr< mc::domains::HyperCylinder< DIM > > motor_housing
mc::geom::Bounds< DIM > getBounds() const override
Get the axis-aligned bounding box of the domain.
Abstract base class for N-dimensional integration domains.
constexpr int dim
Default dimensionality for integration.