// license:GPL-2.0+ // 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" #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 #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 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)); std::size_t nthreads = std::min(static_cast(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_double(m_params.m_max_timestep)); } } template pool_owned_ptr create_it(netlist_state_t &nl, pstring name, solver_parameters_t ¶ms, std::size_t size) { return pool().make_poolptr(nl, name, ¶ms, size); } template pool_owned_ptr NETLIB_NAME(solver)::create_solver(std::size_t size, const pstring &solvername) { if (m_method() == "SOR_MAT") { return create_it>(state(), solvername, m_params, size); //typedef matrix_solver_SOR_mat_t solver_sor_mat; //return plib::make_unique(state(), solvername, &m_params, size); } else if (m_method() == "MAT_CR") { if (size > 0) // GCR always outperforms MAT solver { return create_it>(state(), solvername, m_params, size); } else { return create_it>(state(), solvername, m_params, size); } } else if (m_method() == "MAT") { return create_it>(state(), solvername, m_params, size); } else if (m_method() == "SM") { /* Sherman-Morrison Formula */ return create_it>(state(), solvername, m_params, size); } else if (m_method() == "W") { /* Woodbury Formula */ return create_it>(state(), solvername, m_params, size); } else if (m_method() == "SOR") { return create_it>(state(), solvername, m_params, size); } else if (m_method() == "GMRES") { return create_it>(state(), solvername, m_params, size); } else { log().fatal(MF_UNKNOWN_SOLVER_TYPE(m_method())); return pool_owned_ptr(); } } template pool_owned_ptr NETLIB_NAME(solver)::create_solver_x(std::size_t size, const pstring &solvername) { if (SIZE > 0) { if (size == SIZE) return create_solver(size, solvername); else return this->create_solver_x(size, solvername); } else { if (size * 2 > -SIZE ) return create_solver(size, solvername); else return this->create_solver_x(size, solvername); } } struct net_splitter { 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 (auto & grp : groups) if (plib::container::contains(grp, &n)) return true; return false; } void process_net(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()) { /* 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 = pt->connected_terminal()->net(); if (!already_processed(connected_net)) process_net(connected_net); } } } void run(netlist_state_t &netlist) { for (auto & net : netlist.nets()) { netlist.log().debug("processing {1}\n", net->name()); if (!net->isRailNet() && net->num_cons() > 0) { netlist.log().debug(" ==> not a rail net\n"); /* Must be an analog net */ auto &n = *static_cast(net.get()); if (!already_processed(n)) { groups.emplace_back(analog_net_t::list_t()); process_net(n); } } } } std::vector groups; }; 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(m_gs_loops()); m_params.m_nr_loops = static_cast(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()); // 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 ms; std::size_t net_count = grp.size(); pstring sname = plib::pfmt("Solver_{1}")(m_mat_solvers.size()); switch (net_count) { #if 1 case 1: ms = pool().make_poolptr>(state(), sname, &m_params); break; case 2: ms = pool().make_poolptr>(state(), sname, &m_params); break; case 3: ms = create_solver(3, sname); break; case 4: ms = create_solver(4, sname); break; case 5: ms = create_solver(5, sname); break; case 6: ms = create_solver(6, sname); break; case 7: ms = create_solver(7, sname); break; case 8: ms = create_solver(8, sname); break; case 9: ms = create_solver(9, sname); break; case 10: ms = create_solver(10, sname); break; #if 0 case 11: ms = create_solver(11, sname); break; case 12: ms = create_solver(12, sname); break; case 15: ms = create_solver(15, sname); break; case 31: ms = create_solver(31, sname); break; case 35: ms = create_solver(35, sname); break; case 43: ms = create_solver(43, sname); break; case 49: ms = create_solver(49, sname); break; #endif #if 1 case 86: ms = create_solver(86, sname); break; #endif #endif default: log().info(MI_NO_SPECIFIC_SOLVER(net_count)); if (net_count <= 8) { ms = create_solver(net_count, sname); } else if (net_count <= 16) { ms = create_solver(net_count, sname); } else if (net_count <= 32) { ms = create_solver(net_count, sname); } else if (net_count <= 64) { ms = create_solver(net_count, sname); } else if (net_count <= 128) { ms = create_solver(net_count, sname); } else { log().fatal(MF_NETGROUP_SIZE_EXCEEDED_1(128)); return; /* tease compilers */ } break; } // FIXME ... ms->setup(grp); log().verbose("Solver {1}", ms->name()); log().verbose(" ==> {2} 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