From 1fb5aad029abebb9c06c7678e10267d940723671 Mon Sep 17 00:00:00 2001 From: lschulze <lschulze@mpi-cbg.de> Date: Thu, 8 Jun 2023 20:49:08 +0200 Subject: [PATCH] Upate to new regression module, cleanup --- src/level_set/particle_cp/particle_cp.hpp | 198 ++++++++-------------- 1 file changed, 71 insertions(+), 127 deletions(-) diff --git a/src/level_set/particle_cp/particle_cp.hpp b/src/level_set/particle_cp/particle_cp.hpp index dc157615..3a312cb8 100644 --- a/src/level_set/particle_cp/particle_cp.hpp +++ b/src/level_set/particle_cp/particle_cp.hpp @@ -34,11 +34,7 @@ #include <chrono> #include "/Users/lschulze/Applications/openFPM_Install_master/openfpm_numerics/include/regression/regression.hpp" -aggregate<int, int, double, double[dim], double[n_c]> props; -// | | | | | -// num neibs | sdf sample version interpolation coefficients -// flag for distinguishing closest particles to surface, for which the interpolation has to be done -template<unsigned int dim, unsigned int n_c> using particles_surface = vector_dist<dim, double, props>; +template<unsigned int dim, unsigned int n_c> using particles_surface = vector_dist<dim, double, aggregate<int, int, double, double[dim], double[n_c], EVectorXd>>; struct Redist_options { size_t max_iter = 1000; // params for the Newton algorithm for the solution of the constrained @@ -58,7 +54,11 @@ struct Redist_options float minter_lp_degree = 1.0; int verbose = 0; - int min_num_particles = 0; // if 0, cutoff radius is used, else the int is the minimum number of particles for the regression + int min_num_particles = 0; // if 0, cutoff radius is used, else the int is the minimum number of particles for the regression. + // If min_num_particles is used, the cutoff radius is used for the cell list. It is sensible to use + // a smaller rcut for the celllist, to promote symmetric supports (otherwise it is possible that the + // particle is at the corner of a cell and hence only has neighbors in certain directions) + float r_cutoff_factor_min_num_particles; // this is the rcut for the celllist int only_narrowband = 1; // only redistance particles with phi < sampling_radius, or all particles if only_narrowband = 0 }; @@ -77,7 +77,7 @@ public: vd_in(vd), vd_s(vd.getDecomposition(), 0), r_cutoff2(redistOptions.r_cutoff_factor*redistOptions.r_cutoff_factor*redistOptions.H*redistOptions.H), - minterModelpcp(RegressionModelpcp<vd_s_sdf, decltype(vd_s), decltype(vd_s.getCellList(std::sqrt(r_cutoff2)))>(dim, \ + minterModelpcp(RegressionModel<dim, vd_s_sdf>( redistOptions.minter_poly_degree, redistOptions.minter_lp_degree)) { // initialize polynomial degrees @@ -89,7 +89,7 @@ public: void run_redistancing() { - if (verbose) std::cout<<"Minterpol variable is "<<minterpol<<std::endl; + if (redistOptions.verbose) std::cout<<"Minterpol variable is "<<minterpol<<std::endl; detect_surface_particles(); interpolate_sdf_field(); @@ -103,7 +103,8 @@ private: static constexpr size_t vd_s_sdf = 2; static constexpr size_t vd_s_sample = 3; static constexpr size_t interpol_coeff = 4; - static constexpr size_t vd_s_minter_model = 5; + static constexpr size_t minter_coeff = 5; + // static constexpr size_t vd_s_minter_model = 5; static constexpr size_t vd_in_sdf = phi_field; // this is really required in the vd_in vector, so users need to know about it. static constexpr size_t vd_in_close_part = 4; // this is not needed by the method, but more for debugging purposes, as it shows all particles for which // interpolation and sampling is performed. @@ -127,7 +128,7 @@ private: particles_surface<dim, n_c> vd_s; double r_cutoff2; std::string polynomialDegree; - RegressionModelpcp<vd_s_sdf, particles_surface<dim, n_c>, decltype(vd_s.getCellList(std::sqrt(r_cutoff2)))> minterModelpcp; + RegressionModel<dim, vd_s_sdf> minterModelpcp; int return_sign(double phi) { @@ -155,7 +156,7 @@ private: { vect_dist_key_dx akey = part.get(); // depending on the application this can spare computational effort - if ((only_narrowband) && (std::abs(vd_in.template getProp<vd_in_sdf>(akey)) > redistOptions.sampling_radius)) + if ((redistOptions.only_narrowband) && (std::abs(vd_in.template getProp<vd_in_sdf>(akey)) > redistOptions.sampling_radius)) { ++part; continue; @@ -179,7 +180,6 @@ private: double r2 = norm2(dr); // check if the particle will provide a polynomial interpolation and a sample point on the interface - //if ((sqrt(r2) < (1.3*redistOptions.H)) && !vd_in.template getProp<vd_in_close_part>(bkey) && (sgn_a != sgn_b)) isclose = 1; if ((sqrt(r2) < (1.5*redistOptions.H)) && (sgn_a != sgn_b)) isclose = 1; // count how many particles are in the neighborhood and will be part of the interpolation @@ -199,7 +199,6 @@ private: vd_s.template getLastProp<vd_s_sdf>() = vd_in.template getProp<vd_in_sdf>(akey); vd_s.template getLastProp<num_neibs>() = num_neibs_a; - //if (min_sdf_key.getKey() == akey.getKey()) if (isclose) // these guys will carry an interpolation polynomial and a resulting sample point { vd_s.template getLastProp<vd_s_close_part>() = 1; @@ -215,17 +214,15 @@ private: } } - // todo: maybe detection could be forgone and the interpolation could start off immidiately. - // the detection and decision whether to include as a sample point or not could be done - // within the loop itself. void interpolate_sdf_field() { int message_insufficient_support = 0; int message_projection_fail = 0; vd_s.template ghost_get<vd_s_sdf>(); - auto NN_s = vd_s.getCellList(sqrt(r_cutoff2)); - vd_s.updateCellList(NN_s); + double r_cutoff_celllist = sqrt(r_cutoff2); + if (minterpol && (redistOptions.min_num_particles != 0)) r_cutoff_celllist = redistOptions.r_cutoff_factor_min_num_particles*redistOptions.H; + auto NN_s = vd_s.getCellList(r_cutoff_celllist); auto part = vd_s.getDomainIterator(); // iterate over particles that will get an interpolation polynomial and generate a sample point @@ -247,11 +244,7 @@ private: // initialize monomial basis functions m, Vandermonde row builder vrb, Vandermonde matrix V, the right-hand // side vector phi and the solution vector that contains the coefficients of the interpolation polynomial, c. - // The functions called here are originally from the DCPSE work by Tommaso. I did not fully comprehend what is - // happening inside them, and I kind of made it work for Taylor quadratic, bicubic and Taylor 4 interpolation. - // Possibly, the evaluation of p, dpdx, ... that I implemented could be substituted by the infrastructure that is - // part of the MonomialBasis used here in the interpolation. - + // The functions called here are originally from the DCPSE work. Minter regression is better suited for the application. if (!minterpol){ MonomialBasis <dim> m(4); @@ -288,14 +281,7 @@ private: double r2 = norm2(dr); if (r2 < r_cutoff2) { - // debug given data - //std::cout<<std::setprecision(15)<<xb[0]<<"\t"<<xb[1]<<"\t"<<vd.getProp<sdf>(b)<<std::endl; - //xb[0] = 0.1; - //xb[1] = 0.5; - //xb[2] = 3.0; // Fill phi-vector from the right hand side - //std::cout<<num_neibs_a<<std::endl; - phi[neib] = vd_s.template getProp<vd_s_sdf>(b); vrb.buildRow(V, neib, xb, 1.0); @@ -328,15 +314,20 @@ private: for(int k = 0; k < dim; k++) x[k] = xa[k]; double p = get_p(x, c, polynomialDegree); - // std::cout<<"Evaluation with canonical: p = "<<p<<"\nEvaluation with minter: p = "<<p_minter<<std::endl; - // std::cout<<"Evaluation gradient with canonical: p = "<<grad_p<<"\nEvaluation gradient with minter: p = "<<grad_p_minter<<std::endl; - + EMatrix<double, Eigen::Dynamic, 1> x_debug(dim_r, 1); + for(int k = 0; k < dim_r; k++) x_debug[k] = 0.25; + // do the projections. - // for(int k = 0; k < 3; k++) - // while(incr > 0.01*redistOptions.H) - while ((abs(p) > redistOptions.tolerance) && (k_project < redistOptions.max_iter)) { - grad_p = get_grad_p(x, c, polynomialDegree); + if (((a.getKey() == 9786) || (a.getKey() == 15403)) && (k_project<4)) + { + std::cout<<"x["<<k_project<<"]:"<<std::endl; + std::cout<<x<<std::endl; + std::cout<<"p = "<<get_p(x, c, polynomialDegree)<<std::endl; + std::cout<<"grad p = "<<std::endl; + std::cout<<get_grad_p(x, c, polynomialDegree)<<std::endl; + } + grad_p = get_grad_p(x, c, polynomialDegree); grad_p_mag2 = grad_p.dot(grad_p); x = x - p * grad_p / grad_p_mag2; @@ -344,54 +335,39 @@ private: //incr = sqrt(p*p*dpdx*dpdx/(grad_p_mag2*grad_p_mag2) + p*p*dpdy*dpdy/(grad_p_mag2*grad_p_mag2)); ++k_project; } - //std::cout << "canonical projections yielded\n" << x[0] << ", " << x[1] << ", " << x[2] << "\nafter " - // << k_project << " steps." << std::endl; // store the resulting sample point on the surface at the central particle. for(int k = 0; k < dim; k++) vd_s.template getProp<vd_s_sample>(a)[k] = x[k]; } if (minterpol) { - //auto dom = new RegressionDomain<particles_surface<dim, n_c>, decltype(NN_s)>(vd_s, xa, std::sqrt(r_cutoff2), NN_s); - //std::cout<<"Number of particles in neighborhood: "<<dom->getNumParticles()<<std::endl; - //auto model = new RegressionModel<vd_s_sdf, particles_surface<dim, n_c>, decltype(dom)>(vd_s, dom, 7, 1.0); - //vd_s.template getProp<vd_s_minter_model>(a) = model->return_PolyModel(); - if (num_neibs_a < n_c) message_insufficient_support = 1; - //int verbose_minter = 0; - //if (a.getKey() == 3628) verbose_minter = 1; - minterModelpcp.computeCoeffs(xa, vd_s, r_cutoff2, num_neibs_a, NN_s); - minter::PolyModel minterModel = minterModelpcp.return_PolyModel(); - Eigen::VectorXd coeffs(minterModel.return_coeffs()); - for (int k = 0; k < n_c; k++) vd_s.template getProp<interpol_coeff>(a)[k] = coeffs[k]; - //std::cout<<"original coeffs:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"<<std::endl; - //std::cout<<model->return_PolyModel().coeffs<<std::endl; - //std::cout<<"stored coeffs:%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"<<std::endl; - //std::cout<<vd_s.template getProp<vd_s_minter_model>(a).coeffs<<std::endl; + if(redistOptions.min_num_particles == 0) + { + auto regSupport = RegressionSupport(vd_s, part, sqrt(r_cutoff2), RADIUS, NN_s); + if (regSupport.getNumParticles() < n_c) message_insufficient_support = 1; + minterModelpcp.computeCoeffs(vd_s, regSupport); + } + else + { + auto regSupport = RegressionSupport(vd_s, part, n_c + 3, AT_LEAST_N_PARTICLES, NN_s); + if (regSupport.getNumParticles() < n_c) message_insufficient_support = 1; + minterModelpcp.computeCoeffs(vd_s, regSupport); + } + + auto& minterModel = minterModelpcp.model; + vd_s.template getProp<minter_coeff>(a) = minterModel->getCoeffs(); double grad_p_minter_mag2; EMatrix<double, Eigen::Dynamic, 1> grad_p_minter(dim_r, 1); EMatrix<double, Eigen::Dynamic, 1> x_minter(dim_r, 1); for(int k = 0; k < dim_r; k++) x_minter[k] = xa[k]; - //Point<3, int> derivOrder; - //derivOrder = 0; - //derivOrder[0] = 1; - //std::cout<<"model deriv of wrapper:\n"<<model->deriv(xa, derivOrder)<<std::endl; - //derivOrder = 0; - //derivOrder[1] = 1; - //std::cout<<model->deriv(xa, derivOrder)<<std::endl; - //derivOrder = 0; - //derivOrder[2] = 1; - //std::cout<<model->deriv(xa, derivOrder)<<std::endl; - //std::cout<<"model deriv new:"<<std::endl; - //std::cout<<get_grad_p_minter(x_minter, vd_s.template getProp<vd_s_minter_model>(a))<<std::endl; - //std::cout<<vd_s.template getProp<vd_s_minter_model>(a).deriv_eval(x_minter.transpose(), {0,0,1})<<std::endl; + double p_minter = get_p_minter(x_minter, minterModel); k_project = 0; while ((abs(p_minter) > redistOptions.tolerance) && (k_project < redistOptions.max_iter)) { grad_p_minter = get_grad_p_minter(x_minter, minterModel); - //grad_p_minter_mag2 = grad_p_minter[0]*grad_p_minter[0]+grad_p_minter[1]*grad_p_minter[1]+grad_p_minter[2]*grad_p_minter[2]; grad_p_minter_mag2 = grad_p_minter.dot(grad_p_minter); x_minter = x_minter - p_minter*grad_p_minter/grad_p_minter_mag2; p_minter = get_p_minter(x_minter, minterModel); @@ -400,44 +376,18 @@ private: } // store the resulting sample point on the surface at the central particle. for(int k = 0; k < dim; k++) vd_s.template getProp<vd_s_sample>(a)[k] = x_minter[k]; - //std::cout<<"minter projections yielded\n"<<x_minter[0]<<", "<<x_minter[1]<<", "<<x_minter[2]<<"\nafter "<<k_project<<" steps."<<std::endl; - //std::cout<<"Difference of locations = "<<sqrt((x[0]-x_minter[0])*(x[0]-x_minter[0]) + (x[1]-x_minter[1])*(x[1]-x_minter[1])+(x[2]-x_minter[2])*(x[2]-x_minter[2]))<<std::endl; - //std::cout<<"Ellipsoid equation evaluated at canonical x: "<<- 1 + sqrt((x[0]/0.75)*(x[0]/0.75) + (x[1]/0.5)*(x[1]/0.5) + (x[2]/0.5)*(x[2]/0.5))<<std::endl; - //std::cout<<"Ellipsoid equation evaluated at minter x: "<<- 1 + sqrt((x_minter[0]/0.75)*(x_minter[0]/0.75) + (x_minter[1]/0.5)*(x_minter[1]/0.5) + (x_minter[2]/0.5)*(x_minter[2]/0.5))<<std::endl; - //std::cout<<"################################################################"<<std::endl; } - if (k_project == redistOptions.max_iter) message_projection_fail = 1; - - // debugging stuff: - //if (a.getKey() == 5424) verbose = 1; - //verbose = 0; - if (verbose) + if (k_project == redistOptions.max_iter) { - std::cout<<"VERBOSE for particle "<<a.getKey()<<std::endl; - - //EMatrix<double, Eigen::Dynamic, 1> xaa(dim_r,1); - //for(int k = 0; k < dim; k++) xaa[k] = xa[k]; - //double curvature = get_dpdxdx(xaa, c) + get_dpdydy(xaa, c); - // matlab debugging stuff: - //std::cout<<"p = "<<a.getKey()<<";"<<std::endl; - //std::cout<<std::setprecision(16)<<"A = ["<<V<<"];"<<std::endl; - //std::cout<<"tempcond = cond(A);\ncondnumber = condnumber + tempcond;"<<std::endl; - //std::cout<<"k = k + 1;\nif(tempcond>maxcond)\nmaxcond = tempcond;\nend"<<std::endl; - //std::cout<<"if(tempcond<mincond)\nmincond = tempcond;\nend"<<std::endl; - //std::cout<<"number of coefficients is: "<<m.size()<<std::endl; - //std::cout<<"PHI: \n"<<phi<<std::endl; - //std::cout<<"num neibs: "<<num_neibs_a<<std::endl; - //std::cout<<"x: "<<xa[0]<<" "<<xa[1]<<std::endl; - //std::cout<<"KAPPA: "<<curvature<<std::endl; - //std::cout<<"Vmat\n"<<V<<std::endl; - //verbose = 0; + if (redistOptions.verbose) std::cout<<"didnt work for "<<a.getKey()<<std::endl; + message_projection_fail = 1; } ++part; } if (message_insufficient_support) std::cout<<"Warning: less number of neighbours than required for interpolation" - <<" for some particles. Consider using at least"<<std::endl; + <<" for some particles. Consider using at least N particles function."<<std::endl; if (message_projection_fail) std::cout<<"Warning: Newton-style projections towards the interface do not satisfy" <<" given tolerance for some particles"<<std::endl; @@ -471,7 +421,7 @@ private: { vect_dist_key_dx a = part.get(); - if ((only_narrowband) && (std::abs(vd_in.template getProp<vd_in_sdf>(a)) > redistOptions.sampling_radius)) + if ((redistOptions.only_narrowband) && (std::abs(vd_in.template getProp<vd_in_sdf>(a)) > redistOptions.sampling_radius)) { ++part; continue; @@ -558,9 +508,7 @@ private: for(int k = 0; k < dim; k++) x[k] = vd_s.template getProp<vd_s_sample>(b_min)[k]; // these two variables are mainly required for optional subroutines in the optimization procedure and for - // warnings if the solution strays far from the neighborhood. While the latter is sensible, the optional - // subroutines haven't proven to be especially useful so far, so one should consider throwing them out. - // (Armijo, barrier, random step when step is too big) + // warnings if the solution strays far from the neighborhood. EMatrix<double, Eigen::Dynamic, 1> x00(dim_r,1); for(int k = 0; k < dim; k++) x00[k] = x[k]; @@ -570,25 +518,15 @@ private: // take the interpolation polynomial of the sample particle closest to the query particle for (int k = 0 ; k < n_c ; k++) c[k] = vd_s.template getProp<interpol_coeff>(b_min)[k]; - //if (minterpol) - //auto dom = new RegressionDomain<particles_surface<dim, n_c>, decltype(NN_s)>(vd_s, vd_s.getPos(b_min), std::sqrt(r_cutoff2), NN_s); - //auto model = new RegressionModel<vd_s_sdf, particles_surface<dim, n_c>, decltype(dom)>(vd_s, dom, 4); - minter::PolyModel model = minterModelpcp.return_PolyModel(); + auto& model = minterModelpcp.model; Point<dim, int> derivOrder; Point<dim, double> grad_p_minter; if (minterpol) { - Eigen::VectorXd coeffs(n_c); - for (int k=0; k < n_c; k++) - { - coeffs[k] = vd_s.template getProp<interpol_coeff>(b_min)[k]; - //if ((coeffs[k] > 1e+10)||(std::abs(coeffs[k])<1e-5)) verbose = 1; - } - model.set_coeffs(coeffs); + model->setCoeffs(vd_s.template getProp<minter_coeff>(b_min)); } - // debugging stuff - //if (a.getKey() == 2425) verbose = 1; - if(verbose) + + if(redistOptions.verbose) { std::cout<<std::setprecision(16)<<"VERBOSE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for particle "<<a.getKey()<<std::endl; std::cout<<"x_poly: "<<vd_s.getPos(b_min)[0]<<", "<<vd_s.getPos(b_min)[1]<<"\nxa: "<<xa[0]<<", "<<xa[1]<<"\nx_0: "<<x[0]<<", "<<x[1]<<"\nc: "<<c<<std::endl; @@ -650,9 +588,9 @@ private: // compute Newton increment dx = - H_f.inverse()*nabla_f; - // prevent Newton algorithm from leaving the support radius by scaling step size. This is pretty much - // the only subroutine that should stay. It is invoked in case the increment exceeds 50% of the cutoff - // radius and simply scales the step length down to 10% until it does not exceed 50% of the cutoff radius. + // prevent Newton algorithm from leaving the support radius by scaling step size. It is invoked in case + // the increment exceeds 50% of the cutoff radius and simply scales the step length down to 10% until it + // does not exceed 50% of the cutoff radius. while((dx.dot(dx)) > 0.25*r_cutoff2) { @@ -684,7 +622,7 @@ private: ++k_newton; - if(verbose) + if(redistOptions.verbose) { std::cout<<"dx: "<<dx[0]<<", "<<dx[1]<<std::endl; std::cout<<"H_f:\n"<<H_f<<"\nH_f_inv:\n"<<H_f.inverse()<<std::endl; @@ -717,7 +655,7 @@ private: } // debug optimisation - if(verbose) + if(redistOptions.verbose) { //std::cout<<"new sdf: "<<vd.getProp<sdf>(a)<<std::endl; //std::cout<<"c:\n"<<vd.getProp<interpol_coeff>(a)<<"\nmin_sdf: "<<vd.getProp<min_sdf>(a)<<" , min_sdf_x: "<<vd.getProp<min_sdf_x>(a)<<std::endl; @@ -770,6 +708,7 @@ private: } + // monomial regression polynomial evaluation inline double get_p(EMatrix<double, Eigen::Dynamic, 1> xvector, EMatrix<double, Eigen::Dynamic, 1> c, std::string polynomialdegree) { const double x = xvector[0]; @@ -909,24 +848,29 @@ private: } - inline double get_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, minter::PolyModel &model) + // minterface + template<typename PolyType> + inline double get_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, PolyType model) { - return(model.eval(xvector.transpose())(0)); + return(model->eval(xvector.transpose())(0)); } - inline EMatrix<double, Eigen::Dynamic, 1> get_grad_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, minter::PolyModel &model) + + template<typename PolyType> + inline EMatrix<double, Eigen::Dynamic, 1> get_grad_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, PolyType model) { EMatrix<double, Eigen::Dynamic, 1> grad_p(dim, 1); std::vector<int> derivOrder(dim, 0); for(int k = 0; k < dim; k++){ std::fill(derivOrder.begin(), derivOrder.end(), 0); derivOrder[k] = 1; - grad_p[k] = model.deriv_eval(xvector.transpose(), derivOrder)(0); + grad_p[k] = model->deriv_eval(xvector.transpose(), derivOrder)(0); } return(grad_p); } - inline EMatrix<double, Eigen::Dynamic, Eigen::Dynamic> get_H_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, minter::PolyModel &model) + template<typename PolyType> + inline EMatrix<double, Eigen::Dynamic, Eigen::Dynamic> get_H_p_minter(EMatrix<double, Eigen::Dynamic, 1> xvector, PolyType model) { EMatrix<double, Eigen::Dynamic, Eigen::Dynamic> H_p(dim, dim); std::vector<int> derivOrder(dim, 0); @@ -937,7 +881,7 @@ private: std::fill(derivOrder.begin(), derivOrder.end(), 0); derivOrder[k]++; derivOrder[l]++; - H_p(k, l) = model.deriv_eval(xvector.transpose(), derivOrder)(0); + H_p(k, l) = model->deriv_eval(xvector.transpose(), derivOrder)(0); } } return(H_p); -- GitLab