diff options
Diffstat (limited to 'src/lib/netlist/solver/nld_solver.cpp')
-rw-r--r-- | src/lib/netlist/solver/nld_solver.cpp | 762 |
1 files changed, 475 insertions, 287 deletions
diff --git a/src/lib/netlist/solver/nld_solver.cpp b/src/lib/netlist/solver/nld_solver.cpp index 0aa7e6f584c..d7381cc813a 100644 --- a/src/lib/netlist/solver/nld_solver.cpp +++ b/src/lib/netlist/solver/nld_solver.cpp @@ -1,38 +1,14 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nld_solver.c - * - */ - -/* 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_lists.h" -#include "netlist/nl_factory.h" +// Names +// spell-checker: words Woodbury, + +#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" #include "nld_ms_direct1.h" @@ -43,25 +19,48 @@ #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" #include <algorithm> -#include <cmath> +#include <type_traits> -namespace netlist -{ -namespace devices +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) { + if (exec().stats_enabled()) + m_fb_step.set_delegate(NETLIB_DELEGATE(fb_step<true>)); for (auto &s : m_mat_solvers) s->reset(); + for (auto &s : m_mat_solvers) + m_queue.push<false>({netlist_time_ext::zero(), s.get()}); } void NETLIB_NAME(solver)::stop() @@ -70,352 +69,541 @@ namespace devices s->log_stats(); } - NETLIB_UPDATE(solver) +#if 1 + + template <bool KEEP_STATS> + 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; + + while (!m_queue.empty()) + { + const auto t = m_queue.top().exec_time(); + auto * o = m_queue.top().object(); + if (t != now) + if (t > sched) + break; + tmp[p++] = o; + m_queue.pop(); + } + + // 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"); + } + else + { + stats()->m_stat_total_time.stop(); + for (std::size_t i = 0; i < p; i++) + { + tmp[i]->stats()->m_stat_call_count.inc(); + auto g(tmp[i]->stats()->m_stat_total_time.guard()); + nt[i] = tmp[i]->solve(now, "no-parallel"); + } + stats()->m_stat_total_time.start(); + } + + for (std::size_t i = 0; i < p; i++) + { + if (nt[i] != netlist_time::zero()) + m_queue.push<false>({now + nt[i], tmp[i]}); + tmp[i]->update_inputs(); + } + } + 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"); }); + for (std::size_t i = 0; i < p; i++) + { + if (nt[i] != netlist_time::zero()) + m_queue.push<false>({now + nt[i], tmp[i]}); + tmp[i]->update_inputs(); + } + } + if (!m_queue.empty()) + 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) + { + const netlist_time_ext now(exec().time()); + const netlist_time_ext sched(now + ts); + m_queue.remove<false>(solv); + m_queue.push<false>({sched, solv}); + + if (m_Q_step.net().is_queued()) + { + if (m_Q_step.net().next_scheduled_time() > sched) + m_Q_step.net().toggle_and_push_to_queue(ts); + } + else + m_Q_step.net().toggle_and_push_to_queue(ts); + } +#else + NETLIB_HANDLER(solver, fb_step) { if (m_params.m_dynamic_ts) return; - netlist_time 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::from_double(2 * m_params.m_max_timestep)); + 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)); - std::size_t nthreads = std::min(static_cast<std::size_t>(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<matrix_solver_t *> &solvers = (force_solve ? m_mat_solvers_all : m_mat_solvers_timestepping); + 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) - { - const netlist_time ts = solvers[i]->solve(now); - plib::unused_var(ts); + 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) { - const netlist_time ts = solver->solve(now); - plib::unused_var(ts); + [[maybe_unused]] const netlist_time ts = solver->ptr->solve( + now); } - for (auto & solver : solvers) - solver->update_inputs(); + for (auto &solver : solvers) + solver->ptr->update_inputs(); - /* step circuit */ + // step circuit if (!m_Q_step.net().is_queued()) { - m_Q_step.net().toggle_and_push_to_queue(netlist_time::from_double(m_params.m_max_timestep)); + m_Q_step.net().toggle_and_push_to_queue( + netlist_time::from_fp(m_params.m_max_time_step)); } } +#endif - template <class C> - pool_owned_ptr<matrix_solver_t> create_it(netlist_state_t &nl, pstring name, solver_parameters_t ¶ms, std::size_t size) + // 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) { - return pool().make_poolptr<C>(nl, name, ¶ms, size); + return plib::make_unique<C>(arena, main_solver, name, nets, params, + size); } template <typename FT, int SIZE> - pool_owned_ptr<matrix_solver_t> NETLIB_NAME(solver)::create_solver(std::size_t size, const pstring &solvername) + 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) { - if (m_method() == "SOR_MAT") - { - return create_it<matrix_solver_SOR_mat_t<FT, SIZE>>(state(), solvername, m_params, size); - //typedef matrix_solver_SOR_mat_t<m_N,storage_N> solver_sor_mat; - //return plib::make_unique<solver_sor_mat>(state(), solvername, &m_params, size); - } - else if (m_method() == "MAT_CR") - { - if (size > 0) // GCR always outperforms MAT solver - { - return create_it<matrix_solver_GCR_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else - { - return create_it<matrix_solver_direct_t<FT, SIZE>>(state(), solvername, m_params, size); - } - } - else if (m_method() == "MAT") - { - return create_it<matrix_solver_direct_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else if (m_method() == "SM") - { - /* Sherman-Morrison Formula */ - return create_it<matrix_solver_sm_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else if (m_method() == "W") - { - /* Woodbury Formula */ - return create_it<matrix_solver_w_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else if (m_method() == "SOR") - { - return create_it<matrix_solver_SOR_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else if (m_method() == "GMRES") - { - return create_it<matrix_solver_GMRES_t<FT, SIZE>>(state(), solvername, m_params, size); - } - else + switch (params->m_method()) { - log().fatal(MF_UNKNOWN_SOLVER_TYPE(m_method())); - return pool_owned_ptr<matrix_solver_t>(); + 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); + case solver::matrix_type_e::MAT: + 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); +#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); + 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); + 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); + 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); +#else + // 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); +#endif } + return solver_ptr(); } - template <typename FT, int SIZE> - pool_owned_ptr<matrix_solver_t> NETLIB_NAME(solver)::create_solver_x(std::size_t size, const pstring &solvername) + template <typename FT> + NETLIB_NAME(solver)::solver_ptr NETLIB_NAME(solver)::create_solvers( + const pstring &sname, const solver::solver_parameters_t *params, + net_list_t &nets) { - if (SIZE > 0) - { - if (size == SIZE) - return create_solver<FT, SIZE>(size, solvername); - else - return this->create_solver_x<FT, SIZE-1>(size, solvername); - } - else + std::size_t net_count = nets.size(); + switch (net_count) { - if (size * 2 > -SIZE ) - return create_solver<FT, SIZE>(size, solvername); - else - return this->create_solver_x<FT, SIZE / 2>(size, solvername); +#if !defined(__EMSCRIPTEN__) + case 1: + 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); +#endif + default: + log().info(MI_NO_SPECIFIC_SOLVER(net_count)); + if (net_count <= 16) + { + return create_solver<FT, -16>(net_count, sname, params, + nets); + } + if (net_count <= 32) + { + return create_solver<FT, -32>(net_count, sname, params, + nets); + } + if (net_count <= 64) + { + return create_solver<FT, -64>(net_count, sname, params, + nets); + } + if (net_count <= 128) + { + return create_solver<FT, -128>(net_count, sname, params, + nets); + } + if (net_count <= 256) + { + 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, 0>(net_count, sname, params, nets); } } struct net_splitter { + void run(netlist_state_t &nl_state) + { + 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 = 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)); + } + } + } + for (auto &g : groupspre) + if (!g.empty()) + groups.push_back(g); + } + + 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.isRailNet()) + // 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 (auto & grp : groups) + // if it's already processed - no need to continue + for (const auto &grp : groups) if (plib::container::contains(grp, &n)) return true; return false; } - void process_net(analog_net_t &n) + bool check_if_processed_and_join(const analog_net_t &n) { - /* ignore empty nets. FIXME: print a warning message */ - if (n.num_cons() == 0) - return; - /* add the net */ - groups.back().push_back(&n); - /* process all terminals connected to this net */ - for (auto &term : n.core_terms()) + // no need to process rail nets - these are known variables + if (n.is_rail_net()) + 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) { - /* only process analog terminals */ - if (term->is_type(detail::terminal_type::TERMINAL)) - { - auto *pt = static_cast<terminal_t *>(term); - /* check the connected terminal */ - analog_net_t &connected_net = pt->connected_terminal()->net(); - if (!already_processed(connected_net)) - process_net(connected_net); - } + 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)) + groupspre.back().push_back(cn); + // clear + groupspre[i].clear(); + return true; + } } + // if it's already processed - no need to continue + if (!groupspre.empty() + && plib::container::contains(groupspre.back(), &n)) + return true; + return false; } - void run(netlist_state_t &netlist) + // NOLINTNEXTLINE(misc-no-recursion) + void process_net(netlist_state_t &nl_state, analog_net_t &n) { - for (auto & net : netlist.nets()) + // ignore empty nets. FIXME: print a warning message + nl_state.log().verbose("Net {}", n.name()); + auto terminals(n.core_terms_copy()); + + if (!terminals.empty()) { - netlist.log().debug("processing {1}\n", net->name()); - if (!net->isRailNet() && net->num_cons() > 0) + // add the net + groupspre.back().push_back(&n); + // process all terminals connected to this net + for (detail::core_terminal_t *term : terminals) { - netlist.log().debug(" ==> not a rail net\n"); - /* Must be an analog net */ - auto &n = *static_cast<analog_net_t *>(net.get()); - if (!already_processed(n)) + nl_state.log().verbose("Term {} {}", term->name(), + static_cast<int>(term->type())); + // only process analog terminals + if (term->is_type(detail::terminal_type::TERMINAL)) { - groups.emplace_back(analog_net_t::list_t()); - process_net(n); + 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)); + // NOLINTNEXTLINE proposal does not work for VS + 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()); + if (!check_if_processed_and_join(connected_net)) + process_net(nl_state, connected_net); + } } } } } - std::vector<analog_net_t::list_t> groups; + std::vector<NETLIB_NAME(solver)::net_list_t> groupspre; }; void NETLIB_NAME(solver)::post_start() { - m_params.m_pivot = m_pivot(); - m_params.m_accuracy = m_accuracy(); - /* FIXME: Throw when negative */ - m_params.m_gs_loops = static_cast<unsigned>(m_gs_loops()); - m_params.m_nr_loops = static_cast<unsigned>(m_nr_loops()); - m_params.m_nr_recalc_delay = netlist_time::from_double(m_nr_recalc_delay()); - m_params.m_dynamic_lte = m_dynamic_lte(); - m_params.m_gs_sor = m_gs_sor(); - - m_params.m_min_timestep = m_dynamic_min_ts(); - m_params.m_dynamic_ts = (m_dynamic_ts() == 1 ? true : false); - m_params.m_max_timestep = netlist_time::from_double(1.0 / m_freq()).as_double(); - - m_params.m_use_gabs = m_use_gabs(); - m_params.m_use_linear_prediction = m_use_linear_prediction(); - - - if (m_params.m_dynamic_ts) - { - m_params.m_max_timestep *= 1;//NL_FCONST(1000.0); - } - else - { - m_params.m_min_timestep = m_params.m_max_timestep; - } - - //m_params.m_max_timestep = std::max(m_params.m_max_timestep, m_params.m_max_timestep::) - log().verbose("Scanning net groups ..."); // determine net groups net_splitter splitter; splitter.run(state()); + log().verbose("Found {1} net groups in {2} nets\n", + splitter.groups.size(), state().nets().size()); - // setup the solvers - log().verbose("Found {1} net groups in {2} nets\n", splitter.groups.size(), state().nets().size()); - for (auto & grp : splitter.groups) - { - pool_owned_ptr<matrix_solver_t> ms; - std::size_t net_count = grp.size(); - pstring sname = plib::pfmt("Solver_{1}")(m_mat_solvers.size()); + int num_errors = 0; - switch (net_count) + log().verbose("checking net consistency ..."); + for (const auto &grp : splitter.groups) + { + int rail_terminals = 0; + pstring nets_in_grp; + for (const auto &n : grp) { - #if 1 - case 1: - ms = pool().make_poolptr<matrix_solver_direct1_t<double>>(state(), sname, &m_params); - break; - case 2: - ms = pool().make_poolptr<matrix_solver_direct2_t<double>>(state(), sname, &m_params); - break; - case 3: - ms = create_solver<double, 3>(3, sname); - break; - case 4: - ms = create_solver<double, 4>(4, sname); - break; - case 5: - ms = create_solver<double, 5>(5, sname); - break; - case 6: - ms = create_solver<double, 6>(6, sname); - break; - case 7: - ms = create_solver<double, 7>(7, sname); - break; - case 8: - ms = create_solver<double, 8>(8, sname); - break; - case 9: - ms = create_solver<double, 9>(9, sname); - break; - case 10: - ms = create_solver<double, 10>(10, sname); - break; - #if 0 - case 11: - ms = create_solver<double, 11>(11, sname); - break; - case 12: - ms = create_solver<double, 12>(12, sname); - break; - case 15: - ms = create_solver<double, 15>(15, sname); - break; - case 31: - ms = create_solver<double, 31>(31, sname); - break; - case 35: - ms = create_solver<double, 35>(35, sname); - break; - case 43: - ms = create_solver<double, 43>(43, sname); - break; - case 49: - ms = create_solver<double, 49>(49, sname); - break; - #endif - #if 1 - case 86: - ms = create_solver<double,86>(86, sname); - break; - #endif - #endif - default: - log().info(MI_NO_SPECIFIC_SOLVER(net_count)); - if (net_count <= 8) - { - ms = create_solver<double, -8>(net_count, sname); - } - else if (net_count <= 16) - { - ms = create_solver<double, -16>(net_count, sname); - } - else if (net_count <= 32) - { - ms = create_solver<double, -32>(net_count, sname); - } - else - if (net_count <= 64) - { - ms = create_solver<double, -64>(net_count, sname); - } - else - if (net_count <= 128) + nets_in_grp += (n->name() + " "); + if (!n->is_analog()) + { + 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())); + num_errors++; + } + for (detail::core_terminal_t *t : n->core_terms_copy()) + { + if (!t->has_net()) { - ms = create_solver<double, -128>(net_count, sname); + state().log().error( + ME_SOLVER_TERMINAL_NO_NET(t->name())); + num_errors++; } else { - log().fatal(MF_NETGROUP_SIZE_EXCEEDED_1(128)); - return; /* tease compilers */ + if (auto other_terminal = plib::dynamic_downcast< + terminal_t *>(t)) + if (state() + .setup() + .get_connected_terminal(*(*other_terminal)) + ->net() + .is_rail_net()) + rail_terminals++; } + } + } + if (rail_terminals == 0) + { + state().log().error(ME_SOLVER_NO_RAIL_TERMINAL(nets_in_grp)); + num_errors++; + } + } + if (num_errors > 0) + throw nl_exception(MF_SOLVER_CONSISTENCY_ERRORS(num_errors)); + + // setup the solvers + 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); + + 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); + 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); + 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()); + ms = create_solvers<double>(sname, params.get(), grp); +#endif break; } - // FIXME ... - ms->setup(grp); + 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} elements", ms->has_dynamic_devices() ? "dynamic" : "no dynamic"); - log().verbose(" has {1} elements", ms->has_timestep_devices() ? "timestep" : "no timestep"); + 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()); - for (const auto &pcore : n->core_terms()) + for (const detail::core_terminal_t *t : n->core_terms_copy()) { - log().verbose(" {1}", pcore->name()); + log().verbose(" {1}", t->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)); + m_mat_params.push_back(std::move(params)); + m_mat_solvers.push_back(std::move(ms)); } } - void NETLIB_NAME(solver)::create_solver_code(std::map<pstring, pstring> &mp) + solver::static_compile_container NETLIB_NAME(solver)::create_solver_code( + solver::static_compile_target target) { - for (auto & s : m_mat_solvers) + solver::static_compile_container mp; + for (auto &s : m_mat_solvers) { - auto r = s->create_solver_code(); - mp[r.first] = r.second; // automatically overwrites identical names + auto r = s->create_solver_code(target); + 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 + { + 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 + { + return m_mat_solvers[id].get(); } NETLIB_DEVICE_IMPL(solver, "SOLVER", "FREQ") -} // namespace devices -} // namespace netlist +} // namespace netlist::devices |