// license:GPL-2.0+ // copyright-holders:Couriersud // Commented out for now. Relatively low number of terminals / nets make // the vectorizations fast-math enables pretty expensive // #if 0 #pragma GCC optimize "-ftree-vectorize" #pragma GCC optimize "-ffast-math" #pragma GCC optimize "-funsafe-math-optimizations" #pragma GCC optimize "-funroll-loops" #pragma GCC optimize "-funswitch-loops" #pragma GCC optimize "-fstrict-aliasing" #pragma GCC optimize "tree-vectorizer-verbose=7" #pragma GCC optimize "opt-info-vec" #pragma GCC optimize "opt-info-vec-missed" //#pragma GCC optimize "tree-parallelize-loops=4" #pragma GCC optimize "variable-expansion-in-unroller" #pragma GCC optimize "unsafe-loop-optimizations" #pragma GCC optimize "vect-cost-model" #pragma GCC optimize "variable-expansion-in-unroller" #pragma GCC optimize "tree-loop-if-convert-stores" #pragma GCC optimize "tree-loop-distribution" #pragma GCC optimize "tree-loop-im" #pragma GCC optimize "tree-loop-ivcanon" #pragma GCC optimize "ivopts" #endif #include "netlist/nl_factory.h" #include "netlist/nl_setup.h" // FIXME: only needed for splitter code #include "nld_matrix_solver.h" #include "nld_ms_direct.h" #include "nld_ms_direct1.h" #include "nld_ms_direct2.h" #include "nld_ms_gcr.h" #include "nld_ms_gmres.h" #include "nld_ms_sm.h" #include "nld_ms_sor.h" #include "nld_ms_sor_mat.h" #include "nld_ms_w.h" #include "nld_solver.h" #include "plib/pomp.h" #include namespace netlist { namespace devices { // ---------------------------------------------------------------------------------------- // solver // ---------------------------------------------------------------------------------------- NETLIB_RESET(solver) { for (auto &s : m_mat_solvers) s->reset(); } void NETLIB_NAME(solver)::stop() { for (auto &s : m_mat_solvers) s->log_stats(); } NETLIB_UPDATE(solver) { if (m_params.m_dynamic_ts) return; netlist_time_ext now(exec().time()); // force solving during start up if there are no time-step devices // FIXME: Needs a more elegant solution bool force_solve = (now < netlist_time_ext::from_fp(2 * m_params.m_max_timestep)); std::size_t nthreads = std::min(static_cast(m_params.m_parallel()), plib::omp::get_max_threads()); std::vector &solvers = (force_solve ? m_mat_solvers_all : m_mat_solvers_timestepping); if (nthreads > 1 && solvers.size() > 1) { plib::omp::set_num_threads(nthreads); plib::omp::for_static(static_cast(0), solvers.size(), [&solvers, now](std::size_t i) { const netlist_time ts = solvers[i]->solve(now); plib::unused_var(ts); }); } else for (auto & solver : solvers) { const netlist_time ts = solver->solve(now); plib::unused_var(ts); } for (auto & solver : solvers) solver->update_inputs(); // step circuit if (!m_Q_step.net().is_queued()) { m_Q_step.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_max_timestep)); } } template plib::unique_ptr create_it(netlist_state_t &nl, pstring name, analog_net_t::list_t &nets, solver::solver_parameters_t ¶ms, std::size_t size) { return plib::make_unique(nl, name, nets, ¶ms, size); } template plib::unique_ptr NETLIB_NAME(solver)::create_solver(std::size_t size, const pstring &solvername, analog_net_t::list_t &nets) { switch (m_params.m_method()) { case solver::matrix_type_e::MAT_CR: if (size > 0) // GCR always outperforms MAT solver { return create_it>(state(), solvername, nets, m_params, size); } else { return create_it>(state(), solvername, nets, m_params, size); } case solver::matrix_type_e::SOR_MAT: return create_it>(state(), solvername, nets, m_params, size); case solver::matrix_type_e::MAT: return create_it>(state(), solvername, nets, m_params, size); case solver::matrix_type_e::SM: // Sherman-Morrison Formula return create_it>(state(), solvername, nets, m_params, size); case solver::matrix_type_e::W: // Woodbury Formula return create_it>(state(), solvername, nets, m_params, size); case solver::matrix_type_e::SOR: return create_it>(state(), solvername, nets, m_params, size); case solver::matrix_type_e::GMRES: return create_it>(state(), solvername, nets, m_params, size); } return plib::unique_ptr(); } template plib::unique_ptr NETLIB_NAME(solver)::create_solvers( const pstring &sname, analog_net_t::list_t &nets) { std::size_t net_count = nets.size(); switch (net_count) { case 1: return plib::make_unique>(state(), sname, nets, &m_params); break; case 2: return plib::make_unique>(state(), sname, nets, &m_params); break; #if 1 case 3: return create_solver(3, sname, nets); break; case 4: return create_solver(4, sname, nets); break; case 5: return create_solver(5, sname, nets); break; case 6: return create_solver(6, sname, nets); break; case 7: return create_solver(7, sname, nets); break; case 8: return create_solver(8, sname, nets); break; case 9: return create_solver(9, sname, nets); break; case 10: return create_solver(10, sname, nets); break; #if 0 case 11: return create_solver(11, sname); break; case 12: return create_solver(12, sname); break; case 15: return create_solver(15, sname); break; case 31: return create_solver(31, sname); break; case 35: return create_solver(35, sname); break; case 43: return create_solver(43, sname); break; case 49: return create_solver(49, sname); break; case 87: return create_solver(86, sname, nets); break; #endif #endif default: log().info(MI_NO_SPECIFIC_SOLVER(net_count)); if (net_count <= 8) { return create_solver(net_count, sname, nets); } else if (net_count <= 16) { return create_solver(net_count, sname, nets); } else if (net_count <= 32) { return create_solver(net_count, sname, nets); } else if (net_count <= 64) { return create_solver(net_count, sname, nets); } else if (net_count <= 128) { return create_solver(net_count, sname, nets); } else if (net_count <= 256) { return create_solver(net_count, sname, nets); } else if (net_count <= 512) { return create_solver(net_count, sname, nets); } else { return create_solver(net_count, sname, nets); } break; } } struct net_splitter { void run(netlist_state_t &netlist) { for (auto & net : netlist.nets()) { netlist.log().verbose("processing {1}", net->name()); if (!net->isRailNet() && net->num_cons() > 0) { netlist.log().verbose(" ==> not a rail net"); // Must be an analog net auto &n = *static_cast(net.get()); if (!already_processed(n)) { groupspre.emplace_back(analog_net_t::list_t()); process_net(netlist, n); } } } for (auto &g : groupspre) if (!g.empty()) groups.push_back(g); } std::vector groups; private: bool already_processed(const analog_net_t &n) const { // no need to process rail nets - these are known variables if (n.isRailNet()) return true; // if it's already processed - no need to continue for (const auto & grp : groups) if (plib::container::contains(grp, &n)) return true; return false; } bool check_if_processed_and_join(const analog_net_t &n) { // no need to process rail nets - these are known variables if (n.isRailNet()) return true; // First check if it is in a previous group. // In this case we need to merge this group into the current group if (groupspre.size() > 1) { for (std::size_t i = 0; iname(), static_cast(term->type())); // only process analog terminals if (term->is_type(detail::terminal_type::TERMINAL)) { auto *pt = static_cast(term); // check the connected terminal analog_net_t &connected_net = netlist.setup().get_connected_terminal(*pt)->net(); netlist.log().verbose(" Connected net {}", connected_net.name()); if (!check_if_processed_and_join(connected_net)) process_net(netlist, connected_net); } } } std::vector groupspre; }; void NETLIB_NAME(solver)::post_start() { log().verbose("Scanning net groups ..."); // determine net groups net_splitter splitter; splitter.run(state()); // setup the solvers log().verbose("Found {1} net groups in {2} nets\n", splitter.groups.size(), state().nets().size()); for (auto & grp : splitter.groups) { plib::unique_ptr ms; pstring sname = plib::pfmt("Solver_{1}")(m_mat_solvers.size()); switch (m_params.m_fp_type()) { case solver::matrix_fp_type_e::FLOAT: #if (NL_USE_FLOAT_MATRIX) ms = create_solvers(sname, grp); #else ms = create_solvers(sname, grp); #endif break; case solver::matrix_fp_type_e::DOUBLE: ms = create_solvers(sname, grp); break; case solver::matrix_fp_type_e::LONGDOUBLE: #if (NL_USE_LONG_DOUBLE_MATRIX) ms = create_solvers(sname, grp); #else ms = create_solvers(sname, grp); #endif break; case solver::matrix_fp_type_e::FLOAT128: #if (NL_USE_FLOAT128) ms = create_solvers<__float128>(sname, grp); #else ms = create_solvers(sname, grp); #endif break; } log().verbose("Solver {1}", ms->name()); log().verbose(" ==> {1} nets", grp.size()); log().verbose(" has {1} elements", ms->has_dynamic_devices() ? "dynamic" : "no dynamic"); log().verbose(" has {1} elements", ms->has_timestep_devices() ? "timestep" : "no timestep"); for (auto &n : grp) { log().verbose("Net {1}", n->name()); for (const auto &pcore : n->core_terms()) { log().verbose(" {1}", pcore->name()); } } m_mat_solvers_all.push_back(ms.get()); if (ms->has_timestep_devices()) m_mat_solvers_timestepping.push_back(ms.get()); m_mat_solvers.emplace_back(std::move(ms)); } } void NETLIB_NAME(solver)::create_solver_code(std::map &mp) { for (auto & s : m_mat_solvers) { auto r = s->create_solver_code(); mp[r.first] = r.second; // automatically overwrites identical names } } NETLIB_DEVICE_IMPL(solver, "SOLVER", "FREQ") } // namespace devices } // namespace netlist