This commit is contained in:
Matthias Koefferlein 2026-05-31 16:56:54 +02:00
parent cd942db09a
commit a5c730dfc8
8 changed files with 173 additions and 16 deletions

View File

@ -215,7 +215,27 @@ void Device::set_parameter_value (const std::string &name, const tl::Variant &v)
void Device::set_parameter_value_create (const std::string &name, const tl::Variant &v, bool primary, const tl::Variant &def_value)
{
if (device_class ()) {
set_parameter_value (mp_device_class->parameter_id_for_name_create (name, primary, def_value), v);
tl::Variant dv = def_value;
if (dv.is_nil ()) {
if (v.is_a_string ()) {
dv = tl::Variant (std::string ());
} else if (v.is_double ()) {
dv = tl::Variant (0.0);
} else if (v.is_bool ()) {
dv = tl::Variant (false);
} else if (v.can_convert_to_long ()) {
dv = tl::Variant ((long) 0);
} else if (v.is_list ()) {
dv = tl::Variant::empty_list ();
} else if (v.is_array ()) {
dv = tl::Variant::empty_array ();
}
}
set_parameter_value (mp_device_class->parameter_id_for_name_create (name, primary, dv), v);
}
}

View File

@ -307,9 +307,9 @@ public:
*
* If the name is not valid, the parameter is created with the given primary
* flag and default value. The default value also defines the type of the
* parameter.
* parameter. If not given, the type will be derived from the value v.
*/
void set_parameter_value_create (const std::string &name, const tl::Variant &v, bool primary, const tl::Variant &def_value);
void set_parameter_value_create (const std::string &name, const tl::Variant &v, bool primary, const tl::Variant &def_value = tl::Variant ());
/**
* @brief Used for device combination: join terminals with other device

View File

@ -323,8 +323,8 @@ size_t DeviceClass::parameter_id_for_name_create (const std::string &name, bool
pdef.set_id (m_parameter_definitions.size () - 1);
pdef.set_name (name);
pdef.set_default_value (default_value);
pdef.set_is_primary (primary);
pdef.set_default_value (default_value);
return pdef.id ();
}

View File

@ -790,7 +790,7 @@ std::string Netlist::to_string () const
if (p != pd.begin ()) {
ps += ",";
}
ps += p->name () + "=" + tl::sprintf ("%.12g", d->parameter_value (p->id ()));
ps += p->name () + "=" + d->parameter_value (p->id ()).to_string ();
}
res += std::string (" device ") + tl::to_word_or_quoted_string (d->device_class ()->name ()) + " " + device2string (*d) + " (" + ts + ") (" + ps + ");\n";
}

View File

@ -395,11 +395,13 @@ Class<db::Device> decl_dbDevice (decl_dbNetlistObject, "db", "Device",
"@brief Sets the parameter value for the given parameter name.\n"
"If the parameter name is not valid, an exception is thrown."
) +
gsi::method ("set_parameter_create", (void (db::Device::*) (const std::string &, const tl::Variant &, bool, const tl::Variant &)) &db::Device::set_parameter_value_create, gsi::arg ("param_name"), gsi::arg ("value"), gsi::arg ("primary", true), gsi::arg ("default_value", tl::Variant (0.0), "none"),
gsi::method ("set_parameter_create", &db::Device::set_parameter_value_create, gsi::arg ("param_name"), gsi::arg ("value"), gsi::arg ("primary", true), gsi::arg ("default_value", tl::Variant (), "auto"),
"@brief Sets the parameter value for the given parameter name and creates a new parameter if no parameter is present with this name.\n"
"If a parameter exists already with the given name, the value of that parameter is set to the given value and the other arguments are ignored. "
"If no parameter with the given name exists in the device class's parameter table, a new parameter is created. "
"\\primary indicates that this parameter will be a primary one and \\default_value is the default value for this parameter. "
"The default value also implicitly defines the type of the parameter. A float value will create a float-type parameter, a string value for default will create a string-type parameter.\n"
"In this case, \\primary indicates that this parameter will be a primary one and \\default_value is the default value for this parameter. "
"The default value also implicitly defines the type of the parameter. A float value will create a float-type parameter, a string value for default will create a string-type parameter, "
"hence it should 'fit' to the parameter value. If no default value is given, an automatic default is picked depending on the parameter type."
"\n"
"This method has been introduced in version 0.31.0."
),

View File

@ -379,6 +379,8 @@ TEST(9_DeviceMultipliers)
{
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -424,6 +426,8 @@ TEST(9_DeviceMultipliers)
// read once again, this time with known classes (must not trigger issue-652)
{
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -642,6 +646,8 @@ TEST(17_RecursiveExpansion)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
}
@ -677,6 +683,8 @@ TEST(17_RecursiveExpansion)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17b.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl2);
}
@ -691,6 +699,8 @@ TEST(18_XSchemOutput)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader18.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -725,6 +735,8 @@ TEST(19_ngspice_ref)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -760,6 +772,8 @@ TEST(19b_ngspice_ref)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19b.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -795,6 +809,8 @@ TEST(20_precendence)
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader20.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -933,6 +949,35 @@ TEST(26_comments)
);
}
// String parameters, add unknown parameters
TEST(27_MoreParameters)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader27.cir");
db::NetlistSpiceReader reader;
reader.delegate ()->set_read_all_parameters (true);
tl::InputStream is (path);
reader.read (is, nl);
// "QS" is a string parameter and as we have set "read_all_parameters" to true, the
// devices will receive all the new parameters
EXPECT_EQ (nl.to_string (),
// right now, this instance appears because it is the default incarnation - but not used
"circuit 'CHILD(QS=X,QX=1.5,QY=2)' (A=A,B=B);\n"
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=1.7,AS=0,AD=0,PS=0,PD=0,QS=X,QX=3,QY=4);\n"
"end;\n"
"circuit TOP (N1=N1,N2=N2);\n"
" subcircuit 'CHILD(QS=STR_VALUE,QX=17,QY=2)' '1' (A=N1,B=N2);\n"
"end;\n"
"circuit 'CHILD(QS=STR_VALUE,QX=17,QY=2)' (A=A,B=B);\n"
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=1.7,AS=0,AD=0,PS=0,PD=0,QS=STR_VALUE,QX=18.5,QY=4);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;
@ -1058,3 +1103,4 @@ TEST(101_ExpressionParserWithDefScale)
EXPECT_EQ (parser.read ("1.75k").to_string (), "1750");
EXPECT_EQ (parser.read ("2*A").to_string (), "0.035");
}

View File

@ -354,8 +354,8 @@ TEST(1_DeviceTerminalDefinition)
dc.add_parameter_definition (ppd2);
EXPECT_EQ (ppd2.is_primary (), true);
EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0");
EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1");
EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=##1 #0");
EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=##0 #1");
dc.clear_parameter_definitions ();
EXPECT_EQ (dc.parameter_definitions ().empty (), true);
@ -1854,7 +1854,7 @@ TEST(27_CombineSmallC)
EXPECT_EQ (nl.to_string (),
"circuit TOP (p1=n1,p2=n3);\n"
" device model_name c1 (A=n1,B=n3) (C=3.66666666667e-15,A=0,P=0);\n"
" device model_name c1 (A=n1,B=n3) (C=3.66666666666667e-15,A=0,P=0);\n"
"end;\n"
);
}
@ -2015,17 +2015,17 @@ TEST(28_EliminateShortedDevices)
db::Device *c1 = new db::Device (device, "c1");
circuit->add_device (c1);
c1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1e-15);
c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1e-15);
db::Device *c2 = new db::Device (device, "c2");
circuit->add_device (c2);
c2->set_parameter_value (db::DeviceClassInductor::param_id_L, 2e-15);
c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 2e-15);
c1->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1);
c1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n1);
c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1);
c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n1);
c2->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1);
c2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2);
c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2);
EXPECT_EQ (nl.to_string (),
"circuit TOP (p1=n1,p2=n2);\n"
@ -2042,3 +2042,82 @@ TEST(28_EliminateShortedDevices)
"end;\n"
);
}
TEST(29_DeviceParametersWithArbitraryNameAndType)
{
db::Netlist nl;
db::Circuit *circuit = new db::Circuit ();
circuit->set_name ("TOP");
nl.add_circuit (circuit);
db::DeviceClass *device = new db::DeviceClassCapacitor ();
device->set_name ("model_name");
nl.add_device_class (device);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
auto p1 = circuit->add_pin ("p1");
auto p2 = circuit->add_pin ("p2");
circuit->connect_pin (p1.id (), n1);
circuit->connect_pin (p2.id (), n2);
db::Device *c1 = new db::Device (device, "c1");
circuit->add_device (c1);
c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1e-15);
c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1);
c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n1);
EXPECT_EQ (nl.to_string (),
"circuit TOP (p1=n1,p2=n2);\n"
" device model_name c1 (A=n1,B=n1) (C=1e-15,A=0,P=0);\n"
"end;\n"
);
c1->set_parameter_value_create ("T", std::string ("tvalue"), false, std::string ("x"));
c1->set_parameter_value_create ("N", 17l, true);
c1->set_parameter_value_create ("P1", true, false); // bool
c1->set_parameter_value_create ("P2", 0.5, false); // double
tl::Variant l = tl::Variant::empty_list ();
l.push (tl::Variant (0.5));
c1->set_parameter_value_create ("P3", l, false); // list
tl::Variant a = tl::Variant::empty_array ();
a.insert (tl::Variant ("k"), tl::Variant (17l));
c1->set_parameter_value_create ("P4", a, false); // array
const auto *pdt = device->parameter_definition (device->parameter_id_for_name ("T"));
EXPECT_EQ (pdt->is_primary (), false);
EXPECT_EQ (pdt->default_value ().to_parsable_string (), "'x'");
const auto *pdn = device->parameter_definition (device->parameter_id_for_name ("N"));
EXPECT_EQ (pdn->is_primary (), true);
EXPECT_EQ (pdn->default_value ().to_parsable_string (), "#0");
const auto *pd1 = device->parameter_definition (device->parameter_id_for_name ("P1"));
EXPECT_EQ (pd1->is_primary (), false);
EXPECT_EQ (pd1->default_value ().to_parsable_string (), "false");
const auto *pd2 = device->parameter_definition (device->parameter_id_for_name ("P2"));
EXPECT_EQ (pd2->is_primary (), false);
EXPECT_EQ (pd2->default_value ().to_parsable_string (), "##0");
const auto *pd3 = device->parameter_definition (device->parameter_id_for_name ("P3"));
EXPECT_EQ (pd3->is_primary (), false);
EXPECT_EQ (pd3->default_value ().to_parsable_string (), "()");
const auto *pd4 = device->parameter_definition (device->parameter_id_for_name ("P4"));
EXPECT_EQ (pd4->is_primary (), false);
EXPECT_EQ (pd4->default_value ().to_parsable_string (), "{}");
EXPECT_EQ (nl.to_string (),
"circuit TOP (p1=n1,p2=n2);\n"
" device model_name c1 (A=n1,B=n1) (C=1e-15,A=0,P=0,T=tvalue,N=17,P1=true,P2=0.5,P3=(0.5),P4={k=>17});\n"
"end;\n"
);
}

10
testdata/algo/nreader27.cir vendored Normal file
View File

@ -0,0 +1,10 @@
.subckt child a b qs=x qx=1.5 qy=2
* "qx", "qy" and "qs" are new parameters
m1 a b a b nmos w=1.7u qs=qs qx=qx+1.5 qy=qy*2
.ends
.subckt top n1 n2
x1 n1 n2 child qs=str_value qx=17 qy=2
.ends