summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/solver/nld_solver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/solver/nld_solver.cpp')
-rw-r--r--src/lib/netlist/solver/nld_solver.cpp762
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 &params, 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, &params, 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