build-plug.py "singular matrix" error
Closed this issue · 31 comments
Hello, I am trying to use build-plug.py on a very basic diode clipper schematic.
However as soon as I add the extra series diode D3 to get asymmetric clipping the script errors as follow;
$ python build-plug.py -i diode_clipper_asym.sch --table_neg 1 -x 2.0 --build
Input file 1: diode_clipper_asym.sch
module_id: diode_clipper_asym
['D3', 'DIODE', 'Is=10e-12,mUt=30e-3', 'unnamed_net3', 'GND']
['D1', 'DIODE', 'Is=10e-12,mUt=30e-3', 'unnamed_net1', 'unnamed_net3']
['D2', 'DIODE', 'Is=10e-12,mUt=30e-3', 'GND', 'unnamed_net1']
['R1', 'RESISTOR', '2.2k', 'unnamed_net1', 'unnamed_net2']
['C1', 'CAPACITOR', '4.7u', 'unnamed_net2', 'V1']
['OUT1', 'OUTPUT', 'None', 'unnamed_net1']
['IN1', 'INPUT', 'None', 'V1']
S = ((D(3), 'u3', GND,),
(D(1), 'u1', 'u3',),
(D(2), GND, 'u1',),
(R(1), 'u1', 'u2',),
(C(1), 'u2', 'V1',),
(OUT, 'u1',),
(IN, 'V1',),
)
V = {C(1): 4.7e-6,
D(1): dict(Is=10e-12, mUt=30e-3),
D(2): dict(Is=10e-12, mUt=30e-3),
D(3): dict(Is=10e-12, mUt=30e-3),
R(1): 2.2e3,
}
Traceback (most recent call last):
File "build-plug.py", line 680, in
main(sys.argv[1:])
File "build-plug.py", line 677, in main
dk.build()
File "build-plug.py", line 552, in build
faustdsp, faustui = c1.get_faust_code(filename=str(dspname))
File "./analog.py", line 779, in get_faust_code
self._ensure_filter(symbolic=symbolic)
File "./analog.py", line 417, in _ensure_filter
sim = dk_simulator.SimulatePy(dk_simulator.EquationSystem(p), self.solver, self.dc_method)
File "./dk_simulator.py", line 756, in init
self.Si = self.S.I
File "/usr/lib/python2.7/dist-packages/numpy/matrixlib/defmatrix.py", line 972, in getI
return asmatrix(func(self))
File "/usr/lib/python2.7/dist-packages/scipy/linalg/basic.py", line 687, in inv
raise LinAlgError("singular matrix")
numpy.linalg.linalg.LinAlgError: singular matrix
lex@msi:~/guitarlex/build/gschem-schematics$
Hi
Yes, that is a known issue. dklib fail to create a IIR filter for that case. Still dkbuilder is a experimental tool, and it has some drawbacks. To work around this issue, I use different diode types instead cascaded diodes.
I see.
Btw; It seems the tool generates an IIR filter even if there is no filtering at all in the circuit.
If I remove C1 in the above circuit it still generates a fi.iir in the faust file.
Yes. The first thing we do is calculate the linear response as a IIR filter. Then we measure the response in the non-linear domain. Hence, it's always a IIR filter running into a interpolation table, or for the case the circuit is strict linear (like a EQ or a Wah) simple just a IIR filter.
We've had attempts to create direct C++ code but it turns out that the dsp load of the generated filters was much to high then been usable. So we striped it down to generate faust IIR filter coefficients for linear response.
"The first thing we do is calculate the linear response as a IIR filter."
I see. Is it calculated for the entire circuits linear response?
I mean; most analog circuits have EQ filtering in multiple places.
Often before, between and after several cascaded non-linear sections.
Does the tool maintain that order for us or must we split the circuit into sections and glue the resulting code together afterwards?
You must split the circuit by yourself, but the tool will glue it together to one plugin. Example is shown here:
https://linuxmusicians.com/viewtopic.php?f=44&t=19586
and here one with multiple non-linear parts:
https://linuxmusicians.com/viewtopic.php?f=44&t=19806
Hello,
Ok, I am exploring the possibilities of the dkbuilder tool.
I noticed that feeding it a circuit like;
...it generates the variable IIR filter response but doesn't implement the volume potentiometer.
That is, no code generation for the gain reduction of the pot.
Is that normal behavior?
I guess we have to add that manually?
Btw; do you perhaps know of a method or program under linux to plot the bode response
of an LV2 plugin (or whatever audio plugin format/code)?
Thank you.
That happen when the analyser can't fetch any influence of a variable resistor. Bu, I tried your circuit from above and get it working as expected.
For reference I've attached the schematic I used.
Here is the resulting filter I got out of it:
/*******************************************************************************
**************************** File generated by *********************************
********************************************************************************
./build-plug.py -i voxac30_volume.sch -b
*******************************************************************************/
// generated automatically
// DO NOT MODIFY!
declare id "voxac30_volume";
declare name "voxac30_volume";
declare category "Extern";
declare shortname "voxac30_volume";
declare description "voxac30_volume";
import("stdfaust.lib");
/*******************************************************************************
* voxac30_volume generated by dkbuiler from voxac30_volume.sch
*******************************************************************************/
p1 = pre : fi.iir((b0/a0,b1/a0,b2/a0),(a1/a0,a2/a0)) with {
LogPot(a, x) = ba.if(a, (exp(a * x) - 1) / (exp(a) - 1), x);
Inverted(b, x) = ba.if(b, 1 - x, x);
s = 0.993;
fs = float(ma.SR);
pre = _;
gain = vslider("gain[name:gain][style:knob]", 0.5, 0, 1, 0.01) : Inverted(0) : LogPot(3) : si.smooth(s);
b0 = gain*(-8.34298338411429e-10*pow(fs,2)*gain + fs*(8.34298338411429e-10*fs + 8.34298338411429e-6));
b1 = gain*(1.66859667682286e-9*pow(fs,2)*gain - 1.66859667682286e-9*pow(fs,2));
b2 = gain*(-8.34298338411429e-10*pow(fs,2)*gain + fs*(8.34298338411429e-10*fs - 8.34298338411429e-6));
a0 = fs*(3.33719335364572e-11*fs + 1.03452993963017e-5) + gain*(fs*gain*(-8.35966935088252e-10*fs - 5.00579003046858e-7) + fs*(8.02595001551795e-10*fs - 1.15133170700777e-6) + 0.000834298338411429) + 0.0166859667682286;
a1 = -6.67438670729143e-11*pow(fs,2) + gain*(1.6719338701765e-9*pow(fs,2)*gain - 1.60519000310359e-9*pow(fs,2) + 0.00166859667682286) + 0.0333719335364572;
a2 = fs*(3.33719335364572e-11*fs - 1.03452993963017e-5) + gain*(fs*gain*(-8.35966935088252e-10*fs + 5.00579003046858e-7) + fs*(8.02595001551795e-10*fs + 1.15133170700777e-6) + 0.000834298338411429) + 0.0166859667682286;
};
process = p1 ;
build-plug.py have a option to plot the filter, as well faust have some options to do a plot for the files, other than that Ardour has a option to show the frequency and power response.
For the fun I made quickly the power amp stage of the Voxac30:
voxac30_poweramp.sch.tar.gz
It should replace the stage_03. I build a guitarix plugin from it by:
./build-plug.py -i voxac30_poweramp.sch -g 1 -r 1=0.3 -b
Yes, you are right, the generated code DOES work as intended.
I checked by comparing the plugin's magnitude response with SPICE bode plot of the same circuit.
I was misled by the log potentiometer. Even though I had request a LOG pot it hardly felt like one.
So I changed the C code to scale the slider value as v^3.
This gives a much more realistic feel on the potmeter control.
I will take a look at your vox amp later on:)
Btw; do you still have the same schematics you used on the hoffman plexi;
https://linuxmusicians.com/viewtopic.php?f=44&t=19806?
To make a controller logarithmic, you must add ,a=3
to the value field. were the value (3 - 9) sets the logarithmic scale.
I'll check my schematic folders to find the plexi ones.
Here they are:
Aha!
Are all these things documented somewhere?
I don't want to bother you too much with all these questions.
Btw; Is it ok to continue our discussion here or is there a more appropriate place?
Thank you for the schematics.
This is the right place here, and I'm open to answer all questions about it (and fetch your input). At least we make it open source for that reason.
Documentation is were it all leaks. For the Plexi, for example I use the schematics from Hoffmann as inspiration. I know from all my research in this area that values given in schematics have to be take with care, often, the theoretical value in the circuit drawing differ a lot from the real value. Hence I experience a lot with variations in that field and check the results by plot and ear. That isn't documented anywhere.
Ok, great!
Yes, I'm aware of errors and or differences in schematics vs the real devices.
But I was referring to peculiarities of the tools like the
- a=3..9 log pot,
- cascaded diodes not supported
etc.
That's why I said your input is welcome. I wont said this tool is perfect, the opposite is the case. So, when ever you've a idea to make it better, it will be more then welcome.
re: Log Pot, often I try to recreate a special logarithmic beehive, before I noticed that this controller is simply inverted log.
Beside that, I guess you are aware that I've developed a couple of plugs already with this tool, were I work around the drawbacks it has.
Still, some academical research could improve the dkbuilder for sure.
Yes, I understand that the dkbuilder tool is still in experimental stage.
Nevertheless, I think it's great to have such a tool at all.
I am currently just exploring it's capabilities and limitations.
The best way of doing that IMO is to actually use the tool and see what comes out.
For example;
I've build your hoffman PlexiPowerAmp and noticed an absurd low value for R3 100 Ohm.
That is the negative feedback resistor.
The lowest I have seen in real devices is ~1k in fender amps.
Marshall is usually 47k or 100k.
Then when I change the value in the schematic to 47k, build the plug and run it,
the Presence control has barely any effect.
Back to 100 ohm and Presence works.
Any ideas?
That may be relate to the windings or the resistance of the transformer, but I'm not sure. Never found some useful resources about that. Maybe reduce the resistance of the transformer may help here, maybe implement inductors in the signal flow, check out the jcm800_power.sch for a more complex implementation of a power amp. Unfortunately that one is to complex to generate a usable filter, but is the most correct we've ever done.
At least I come to the 100 ohm by experimenting to get the presence working at all.
Might indeed be the output resistance of the transformer. It's currently set to 5400 Ohm in the plexi.
That seems way to high if you imagine a speaker load of 8 ohm don't you think?
Wait a second, shoudn't the transformer output be loaded by an 8 Ohm load for the SPICE simulation?
Oh wait, the 5400 Ohm is probably the primary winding.
Does dkbuilder model Miller Capacitance and Slew Rate in op-amps, transistors, tubes etc.?
dkbuilder is based on this thesis: https://www.vut.cz/www_base/zav_prace_soubor_verejne.php?file_id=60090
you'll find our implementation for the models in DK/models.py
Thanks Hermann for the pointers.
Ok, so parasitic capacitance (Miller effect) is not simulated/modeled.
This is something to consider since it plays a huge role in the frequency response of analog circuitry.
Take for example, the second stage grid in Marshall plexi amps.
That grid is fed through a large 470k mixer resistor.
Together with the ~200pF Miller grid capacitance this forms a low-pass filter with cutoff frequency of ~1690 Hz!
If left out of the simulation it will result in a huge sonic inaccuracy.
(in the above case; way too much treble on the lead channel)
Of course the above example is easily fixed by adding a 200pF C in parallel to the grid,
but I haven't seen anyone doing this in the schematics yet.
Anyway, I thought I mention it here so that new users of dkbuilder are aware of this limitation.
The workaround is often easy though.
I've been following this thread with interest. I don't have much to contribute as I'm not a mathematician or an electrical engineer, but as a programmer/guitarist I would very much like to see progress on the performance and accessibility of tools like dkbuilder.
Ok, so parasitic capacitance (Miller effect) is not simulated/modeled.
This is something to consider since it plays a huge role in the frequency response of analog circuitry.
Yes, you are right. If you've a idea how we could implement it, that would be welcome.
What I do to work around this issue is, doing a frequency plot of the circuit, compare the result with the expected one, and add extra filters to compensate the diff. Hence that's why I've add 11kHz low-pass filters in the plexi simulation.
The correct place to add miller cap. is probably in the models.
But I have currently no knowledge of the model system in use.
As a workaround you can simply add a parasitic capacitor to the schematic at the correct place.
The way you did it in your plexi sim is adding an extra RC filter (150Ohm, 0.1u).
Adding a 200pF cap. parallel to the triode grid is a more realistic simulation provided you leave any series resistance/impedance in-place.
For example, the well known 68k input resistor in conjunction with the miller cap will then automatically form the 12k low-pass.
Hermann, Is there a way to set the wiper position of a potmeter in the schematics?
It seems to me it is currently fixed at 50%.
This influences the generated non-lin tables greatly.
Sorry, nope. I sometimes append a extra active volume control circuit and replace the potentiometer with a resistor,
Ok.
I am currently studying the python code.
Is there any particular reason why the tables are split in pos and neg halves?
I mean; why not 1 single table?
Me again,
I noticed that if a filter is incorporated in the non-linear circuit,
for example the cathode resistor bypass C in a triode circuit, the resulting stage gain is
computed twice!
Once in the IIR filter and once in the table generation.
Resulting in a unrealistic huge stage gain in de faust code.
How to fix?
Leaving out the bypass C in the circuit during table generation?
I am not sure if that would lead to simulation inaccuracy.
Scaling the filter output down? By how much?
Why not normalize the frequency response once a non-lin circuit is detected, and leave the gain to the tables?
Just thinking out loud I guess.
Is there any particular reason why the tables are split in pos and neg halves?
It's a way easier and lighter to implement it in C/C++ this way.
I noticed that if a filter is incorporated in the non-linear circuit, for example the cathode resistor bypass C in a triode circuit, the resulting stage gain is computed twice! Once in the IIR filter and once in the table generation. Resulting in a unrealistic huge stage gain in de faust code.
How to fix?
Not really, It may be that already the IIR filter is way to loud, I guess that's because of the missing compression. That could be solved by adjust the table operator. ( -o ) I didn't find a way to automate that factor, so a general factor is used. So using -o 0.1 will reduce the operator and therewith the gain nonlinear (compression). Additional there is the --reduce_gain (-r ) flag witch will reduce the gain linear.