diff options
Diffstat (limited to 'src/lib/netlist/solver/nld_solver.cpp')
-rw-r--r-- | src/lib/netlist/solver/nld_solver.cpp | 317 |
1 files changed, 205 insertions, 112 deletions
diff --git a/src/lib/netlist/solver/nld_solver.cpp b/src/lib/netlist/solver/nld_solver.cpp index 445608a2c27..d7381cc813a 100644 --- a/src/lib/netlist/solver/nld_solver.cpp +++ b/src/lib/netlist/solver/nld_solver.cpp @@ -4,9 +4,10 @@ // Names // spell-checker: words Woodbury, -#include "nl_factory.h" -#include "core/setup.h" +#include "nld_solver.h" + #include "nl_errstr.h" +#include "nl_factory.h" #include "nl_setup.h" // FIXME: only needed for splitter code #include "nld_matrix_solver.h" #include "nld_ms_direct.h" @@ -18,7 +19,9 @@ #include "nld_ms_sor.h" #include "nld_ms_sor_mat.h" #include "nld_ms_w.h" -#include "nld_solver.h" + +#include "core/setup.h" + #include "plib/pomp.h" #include "plib/ptimed_queue.h" @@ -28,9 +31,27 @@ namespace netlist::devices { - // ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- // solver - // ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + + nld_solver::nld_solver(constructor_param_t data) + : device_t(data) + , m_fb_step(*this, "FB_step", NETLIB_DELEGATE(fb_step<false>)) + , m_Q_step(*this, "Q_step") + , m_params(*this, "", solver::solver_parameter_defaults::get_instance()) + , m_queue( + this->state().pool(), config::max_solver_queue_size(), + queue_type::id_delegate(&NETLIB_NAME(solver)::get_solver_id, this), + queue_type::obj_delegate(&NETLIB_NAME(solver)::solver_by_id, this)) + { + // internal stuff + state().save(*this, + static_cast<plib::state_manager_t::callback_t &>(m_queue), + this->name(), "m_queue"); + + connect("FB_step", "Q_step"); + } NETLIB_RESET(solver) { @@ -54,16 +75,28 @@ namespace netlist::devices NETLIB_HANDLER(solver, fb_step) { const netlist_time_ext now(exec().time()); - const std::size_t nthreads = m_params.m_parallel() < 2 ? 1 : std::min(static_cast<std::size_t>(m_params.m_parallel()), plib::omp::get_max_threads()); - const netlist_time_ext sched(now + (nthreads <= 1 ? netlist_time_ext::zero() : netlist_time_ext::from_nsec(100))); - plib::uninitialised_array<solver::matrix_solver_t *, config::max_solver_queue_size::value> tmp; //NOLINT - plib::uninitialised_array<netlist_time, config::max_solver_queue_size::value> nt; //NOLINT - std::size_t p=0; + const std::size_t nthreads = m_params.m_parallel() < 2 + ? 1 + : std::min( + static_cast<std::size_t>( + m_params.m_parallel()), + plib::omp::get_max_threads()); + const netlist_time_ext sched( + now + + (nthreads <= 1 ? netlist_time_ext::zero() + : netlist_time_ext::from_nsec(100))); + plib::uninitialised_array<solver::matrix_solver_t *, + config::max_solver_queue_size::value> + tmp; // NOLINT + plib::uninitialised_array<netlist_time, + config::max_solver_queue_size::value> + nt; // NOLINT + std::size_t p = 0; while (!m_queue.empty()) { const auto t = m_queue.top().exec_time(); - auto *o = m_queue.top().object(); + auto * o = m_queue.top().object(); if (t != now) if (t > sched) break; @@ -71,14 +104,15 @@ namespace netlist::devices m_queue.pop(); } - // FIXME: Disabled for now since parallel processing will decrease performance + // FIXME: Disabled for now since parallel processing will decrease + // performance // for tested applications. More testing required here if (true || nthreads < 2) { if (!KEEP_STATS) { for (std::size_t i = 0; i < p; i++) - nt[i] = tmp[i]->solve(now, "no-parallel"); + nt[i] = tmp[i]->solve(now, "no-parallel"); } else { @@ -102,10 +136,9 @@ namespace netlist::devices else { plib::omp::set_num_threads(nthreads); - plib::omp::for_static(static_cast<std::size_t>(0), p, [&tmp, &nt,now](std::size_t i) - { - nt[i] = tmp[i]->solve(now, "parallel"); - }); + plib::omp::for_static(static_cast<std::size_t>(0), p, + [&tmp, &nt, now](std::size_t i) + { nt[i] = tmp[i]->solve(now, "parallel"); }); for (std::size_t i = 0; i < p; i++) { if (nt[i] != netlist_time::zero()) @@ -114,10 +147,12 @@ namespace netlist::devices } } if (!m_queue.empty()) - m_Q_step.net().toggle_and_push_to_queue(static_cast<netlist_time>(m_queue.top().exec_time() - now)); + m_Q_step.net().toggle_and_push_to_queue( + static_cast<netlist_time>(m_queue.top().exec_time() - now)); } - void NETLIB_NAME(solver) :: reschedule(solver::matrix_solver_t *solv, netlist_time ts) + void NETLIB_NAME(solver)::reschedule(solver::matrix_solver_t *solv, + netlist_time ts) { const netlist_time_ext now(exec().time()); const netlist_time_ext sched(now + ts); @@ -141,78 +176,101 @@ namespace netlist::devices 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<decltype(m_params.m_max_time_step)>(2 * m_params.m_max_time_step)); + bool force_solve = (now < netlist_time_ext::from_fp< + decltype(m_params.m_max_time_step)>( + 2 * m_params.m_max_time_step)); - std::size_t nthreads = std::min(static_cast<std::size_t>(m_params.m_parallel()), plib::omp::get_max_threads()); + std::size_t nthreads = std::min( + static_cast<std::size_t>(m_params.m_parallel()), + plib::omp::get_max_threads()); - std::vector<solver_entry *> &solvers = (force_solve ? m_mat_solvers_all : m_mat_solvers_time_stepping); + std::vector<solver_entry *> + &solvers = (force_solve ? m_mat_solvers_all + : m_mat_solvers_time_stepping); if (nthreads > 1 && solvers.size() > 1) { plib::omp::set_num_threads(nthreads); - plib::omp::for_static(static_cast<std::size_t>(0), solvers.size(), [&solvers, now](std::size_t i) - { - [[maybe_unused]] const netlist_time ts = solvers[i]->ptr->solve(now); + plib::omp::for_static( + static_cast<std::size_t>(0), solvers.size(), + [&solvers, now](std::size_t i) { + [[maybe_unused]] const netlist_time ts = solvers[i] + ->ptr->solve( + now); }); } else - for (auto & solver : solvers) + for (auto &solver : solvers) { - [[maybe_unused]] const netlist_time ts = solver->ptr->solve(now); + [[maybe_unused]] const netlist_time ts = solver->ptr->solve( + now); } - for (auto & solver : solvers) + for (auto &solver : solvers) solver->ptr->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_time_step)); + m_Q_step.net().toggle_and_push_to_queue( + netlist_time::from_fp(m_params.m_max_time_step)); } } #endif // FIXME: should be created in device space template <class C, class A> - NETLIB_NAME(solver)::solver_ptr create_it(A &arena, NETLIB_NAME(solver) &main_solver, pstring name, - NETLIB_NAME(solver)::net_list_t &nets, - const solver::solver_parameters_t *params, std::size_t size) + NETLIB_NAME(solver)::solver_ptr + create_it(A &arena, NETLIB_NAME(solver) &main_solver, pstring name, + NETLIB_NAME(solver)::net_list_t & nets, + const solver::solver_parameters_t *params, std::size_t size) { - return plib::make_unique<C>(arena, main_solver, name, nets, params, size); + return plib::make_unique<C>(arena, main_solver, name, nets, params, + size); } template <typename FT, int SIZE> - NETLIB_NAME(solver)::solver_ptr NETLIB_NAME(solver)::create_solver(std::size_t size, - const pstring &solver_name, const solver::solver_parameters_t *params, - NETLIB_NAME(solver)::net_list_t &nets) + NETLIB_NAME(solver)::solver_ptr NETLIB_NAME(solver)::create_solver( + std::size_t size, const pstring &solver_name, + const solver::solver_parameters_t *params, + NETLIB_NAME(solver)::net_list_t & nets) { switch (params->m_method()) { case solver::matrix_type_e::MAT_CR: - return create_it<solver::matrix_solver_GCR_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_GCR_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); case solver::matrix_type_e::MAT: - return create_it<solver::matrix_solver_direct_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_direct_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); case solver::matrix_type_e::GMRES: - return create_it<solver::matrix_solver_GMRES_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_GMRES_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); #if (NL_USE_ACADEMIC_SOLVERS) case solver::matrix_type_e::SOR: - return create_it<solver::matrix_solver_SOR_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_SOR_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); case solver::matrix_type_e::SOR_MAT: - return create_it<solver::matrix_solver_SOR_mat_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_SOR_mat_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); case solver::matrix_type_e::SM: // Sherman-Morrison Formula - return create_it<solver::matrix_solver_sm_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_sm_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); case solver::matrix_type_e::W: // Woodbury Formula - return create_it<solver::matrix_solver_w_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + return create_it<solver::matrix_solver_w_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); #else - //case solver::matrix_type_e::GMRES: + // case solver::matrix_type_e::GMRES: case solver::matrix_type_e::SOR: case solver::matrix_type_e::SOR_MAT: case solver::matrix_type_e::SM: case solver::matrix_type_e::W: - state().log().warning(MW_SOLVER_METHOD_NOT_SUPPORTED(params->m_method().name(), "MAT_CR")); - return create_it<solver::matrix_solver_GCR_t<FT, SIZE>>(state().pool(), *this, solver_name, nets, params, size); + state().log().warning(MW_SOLVER_METHOD_NOT_SUPPORTED( + params->m_method().name(), "MAT_CR")); + return create_it<solver::matrix_solver_GCR_t<FT, SIZE>>( + state().pool(), *this, solver_name, nets, params, size); #endif } return solver_ptr(); @@ -228,47 +286,49 @@ namespace netlist::devices { #if !defined(__EMSCRIPTEN__) case 1: - return plib::make_unique<solver::matrix_solver_direct1_t<FT>>(state().pool(), *this, sname, nets, params); + return plib::make_unique<solver::matrix_solver_direct1_t<FT>>( + state().pool(), *this, sname, nets, params); case 2: - return plib::make_unique<solver::matrix_solver_direct2_t<FT>>(state().pool(), *this, sname, nets, params); - case 3: - return create_solver<FT, 3>(3, sname, params, nets); - case 4: - return create_solver<FT, 4>(4, sname, params, nets); - case 5: - return create_solver<FT, 5>(5, sname, params, nets); - case 6: - return create_solver<FT, 6>(6, sname, params, nets); - case 7: - return create_solver<FT, 7>(7, sname, params, nets); - case 8: - return create_solver<FT, 8>(8, sname, params, nets); + return plib::make_unique<solver::matrix_solver_direct2_t<FT>>( + state().pool(), *this, sname, nets, params); + case 3: return create_solver<FT, 3>(3, sname, params, nets); + case 4: return create_solver<FT, 4>(4, sname, params, nets); + case 5: return create_solver<FT, 5>(5, sname, params, nets); + case 6: return create_solver<FT, 6>(6, sname, params, nets); + case 7: return create_solver<FT, 7>(7, sname, params, nets); + case 8: return create_solver<FT, 8>(8, sname, params, nets); #endif default: log().info(MI_NO_SPECIFIC_SOLVER(net_count)); if (net_count <= 16) { - return create_solver<FT, -16>(net_count, sname, params, nets); + return create_solver<FT, -16>(net_count, sname, params, + nets); } if (net_count <= 32) { - return create_solver<FT, -32>(net_count, sname, params, nets); + return create_solver<FT, -32>(net_count, sname, params, + nets); } if (net_count <= 64) { - return create_solver<FT, -64>(net_count, sname, params, nets); + return create_solver<FT, -64>(net_count, sname, params, + nets); } if (net_count <= 128) { - return create_solver<FT, -128>(net_count, sname, params, nets); + return create_solver<FT, -128>(net_count, sname, params, + nets); } if (net_count <= 256) { - return create_solver<FT, -256>(net_count, sname, params, nets); + return create_solver<FT, -256>(net_count, sname, params, + nets); } if (net_count <= 512) { - return create_solver<FT, -512>(net_count, sname, params, nets); + return create_solver<FT, -512>(net_count, sname, params, + nets); } return create_solver<FT, 0>(net_count, sname, params, nets); } @@ -278,18 +338,21 @@ namespace netlist::devices { void run(netlist_state_t &nl_state) { - for (auto & net : nl_state.nets()) + for (auto &net : nl_state.nets()) { nl_state.log().verbose("processing {1}", net->name()); if (!net->is_rail_net() && !net->core_terms_empty()) { nl_state.log().verbose(" ==> not a rail net"); // Must be an analog net - auto &n = dynamic_cast<analog_net_t &>(*net); - if (!already_processed(n)) + auto n = plib::dynamic_downcast<analog_net_t *>(net.get()); + nl_assert_always(bool(n), + "Unable to cast to analog_net_t &"); + if (!already_processed(*(*n))) { - groupspre.emplace_back(NETLIB_NAME(solver)::net_list_t()); - process_net(nl_state, n); + groupspre.emplace_back( + NETLIB_NAME(solver)::net_list_t()); + process_net(nl_state, *(*n)); } } } @@ -301,14 +364,13 @@ namespace netlist::devices std::vector<NETLIB_NAME(solver)::net_list_t> groups; private: - bool already_processed(const analog_net_t &n) const { // no need to process rail nets - these are known variables if (n.is_rail_net()) return true; // if it's already processed - no need to continue - for (const auto & grp : groups) + for (const auto &grp : groups) if (plib::container::contains(grp, &n)) return true; return false; @@ -323,12 +385,13 @@ namespace netlist::devices // In this case we need to merge this group into the current group if (groupspre.size() > 1) { - for (std::size_t i = 0; i<groupspre.size() - 1; i++) + for (std::size_t i = 0; i < groupspre.size() - 1; i++) if (plib::container::contains(groupspre[i], &n)) { // copy all nets - for (auto & cn : groupspre[i]) - if (!plib::container::contains(groupspre.back(), cn)) + for (auto &cn : groupspre[i]) + if (!plib::container::contains(groupspre.back(), + cn)) groupspre.back().push_back(cn); // clear groupspre[i].clear(); @@ -336,7 +399,8 @@ namespace netlist::devices } } // if it's already processed - no need to continue - if (!groupspre.empty() && plib::container::contains(groupspre.back(), &n)) + if (!groupspre.empty() + && plib::container::contains(groupspre.back(), &n)) return true; return false; } @@ -353,20 +417,26 @@ namespace netlist::devices // add the net groupspre.back().push_back(&n); // process all terminals connected to this net - for (detail::core_terminal_t * term : terminals) + for (detail::core_terminal_t *term : terminals) { - nl_state.log().verbose("Term {} {}", term->name(), static_cast<int>(term->type())); + nl_state.log().verbose("Term {} {}", term->name(), + static_cast<int>(term->type())); // only process analog terminals if (term->is_type(detail::terminal_type::TERMINAL)) { - auto &pt = dynamic_cast<terminal_t &>(*term); + auto pt = plib::dynamic_downcast<terminal_t *>(term); + nl_assert_always(bool(pt), + "Error casting *term to terminal_t &"); // check the connected terminal - const auto *const connected_terminals = nl_state.setup().get_connected_terminals(pt); + const auto *const connected_terminals + = nl_state.setup().get_connected_terminals(*(*pt)); // NOLINTNEXTLINE proposal does not work for VS - for (auto ct = connected_terminals->begin(); *ct != nullptr; ct++) + for (auto ct = connected_terminals->begin(); + *ct != nullptr; ct++) { analog_net_t &connected_net = (*ct)->net(); - nl_state.log().verbose(" Connected net {}", connected_net.name()); + nl_state.log().verbose(" Connected net {}", + connected_net.name()); if (!check_if_processed_and_join(connected_net)) process_net(nl_state, connected_net); } @@ -386,40 +456,48 @@ namespace netlist::devices net_splitter splitter; splitter.run(state()); - log().verbose("Found {1} net groups in {2} nets\n", splitter.groups.size(), state().nets().size()); + log().verbose("Found {1} net groups in {2} nets\n", + splitter.groups.size(), state().nets().size()); int num_errors = 0; log().verbose("checking net consistency ..."); for (const auto &grp : splitter.groups) { - int rail_terminals = 0; + int rail_terminals = 0; pstring nets_in_grp; for (const auto &n : grp) { nets_in_grp += (n->name() + " "); if (!n->is_analog()) { - state().log().error(ME_SOLVER_CONSISTENCY_NOT_ANALOG_NET(n->name())); + state().log().error( + ME_SOLVER_CONSISTENCY_NOT_ANALOG_NET(n->name())); num_errors++; } if (n->is_rail_net()) { - state().log().error(ME_SOLVER_CONSISTENCY_RAIL_NET(n->name())); + state().log().error( + ME_SOLVER_CONSISTENCY_RAIL_NET(n->name())); num_errors++; } - for (detail::core_terminal_t * t : n->core_terms_copy()) + for (detail::core_terminal_t *t : n->core_terms_copy()) { if (!t->has_net()) { - state().log().error(ME_SOLVER_TERMINAL_NO_NET(t->name())); + state().log().error( + ME_SOLVER_TERMINAL_NO_NET(t->name())); num_errors++; } else { - auto *other_terminal = dynamic_cast<terminal_t *>(t); - if (other_terminal != nullptr) - if (state().setup().get_connected_terminal(*other_terminal)->net().is_rail_net()) + if (auto other_terminal = plib::dynamic_downcast< + terminal_t *>(t)) + if (state() + .setup() + .get_connected_terminal(*(*other_terminal)) + ->net() + .is_rail_net()) rail_terminals++; } } @@ -433,43 +511,56 @@ namespace netlist::devices if (num_errors > 0) throw nl_exception(MF_SOLVER_CONSISTENCY_ERRORS(num_errors)); - // setup the solvers - for (auto & grp : splitter.groups) + for (auto &grp : splitter.groups) { - solver_ptr ms; - pstring sname = plib::pfmt("Solver_{1}")(m_mat_solvers.size()); - params_uptr params = plib::make_unique<solver::solver_parameters_t>(state().pool(), *this, sname + ".", m_params); + solver_ptr ms; + pstring sname = plib::pfmt("Solver_{1}")(m_mat_solvers.size()); + params_uptr params = plib::make_unique<solver::solver_parameters_t>( + state().pool(), *this, sname + ".", m_params); switch (params->m_fp_type()) { case solver::matrix_fp_type_e::FLOAT: if (!config::use_float_matrix::value) - log().info("FPTYPE {1} not supported. Using DOUBLE", params->m_fp_type().name()); - ms = create_solvers<std::conditional_t<config::use_float_matrix::value, float, double>>(sname, params.get(), grp); + log().info("FPTYPE {1} not supported. Using DOUBLE", + params->m_fp_type().name()); + ms = create_solvers<std::conditional_t< + config::use_float_matrix::value, float, double>>( + sname, params.get(), grp); break; case solver::matrix_fp_type_e::DOUBLE: ms = create_solvers<double>(sname, params.get(), grp); break; case solver::matrix_fp_type_e::LONGDOUBLE: if (!config::use_long_double_matrix::value) - log().info("FPTYPE {1} not supported. Using DOUBLE", params->m_fp_type().name()); - ms = create_solvers<std::conditional_t<config::use_long_double_matrix::value, long double, double>>(sname, params.get(), grp); + log().info("FPTYPE {1} not supported. Using DOUBLE", + params->m_fp_type().name()); + ms = create_solvers<std::conditional_t< + config::use_long_double_matrix::value, long double, + double>>(sname, params.get(), grp); break; case solver::matrix_fp_type_e::FLOATQ128: #if (NL_USE_FLOAT128) ms = create_solvers<FLOAT128>(sname, params.get(), grp); #else - log().info("FPTYPE {1} not supported. Using DOUBLE", params->m_fp_type().name()); + log().info("FPTYPE {1} not supported. Using DOUBLE", + params->m_fp_type().name()); ms = create_solvers<double>(sname, params.get(), grp); #endif break; } + state().register_device( + ms->name(), + device_arena::owned_ptr<core_device_t>(ms.get(), false)); + log().verbose("Solver {1}", ms->name()); log().verbose(" ==> {1} nets", grp.size()); - log().verbose(" has {1} dynamic elements", ms->dynamic_device_count()); - log().verbose(" has {1} time step elements", ms->time_step_device_count()); + log().verbose(" has {1} dynamic elements", + ms->dynamic_device_count()); + log().verbose(" has {1} time step elements", + ms->time_step_device_count()); for (auto &n : grp) { log().verbose("Net {1}", n->name()); @@ -482,35 +573,37 @@ namespace netlist::devices m_mat_params.push_back(std::move(params)); m_mat_solvers.push_back(std::move(ms)); } - } - solver::static_compile_container NETLIB_NAME(solver)::create_solver_code(solver::static_compile_target target) + solver::static_compile_container NETLIB_NAME(solver)::create_solver_code( + solver::static_compile_target target) { solver::static_compile_container mp; - for (auto & s : m_mat_solvers) + for (auto &s : m_mat_solvers) { auto r = s->create_solver_code(target); - if (!r.first.empty()) // ignore solvers not supporting static compile + if (!r.first.empty()) // ignore solvers not supporting static + // compile mp.push_back(r); } return mp; } - std::size_t NETLIB_NAME(solver)::get_solver_id(const solver::matrix_solver_t *net) const + std::size_t NETLIB_NAME(solver)::get_solver_id( + const solver::matrix_solver_t *net) const { - for (std::size_t i=0; i < m_mat_solvers.size(); i++) + for (std::size_t i = 0; i < m_mat_solvers.size(); i++) if (m_mat_solvers[i].get() == net) return i; return std::numeric_limits<std::size_t>::max(); } - solver::matrix_solver_t * NETLIB_NAME(solver)::solver_by_id(std::size_t id) const + solver::matrix_solver_t *NETLIB_NAME(solver)::solver_by_id( + std::size_t id) const { return m_mat_solvers[id].get(); } - NETLIB_DEVICE_IMPL(solver, "SOLVER", "FREQ") } // namespace netlist::devices |