missing NULL terminator in set_config_x
[geda-gaf.git] / xorn / src / backend / gnet_spice_sdb.py
blob698c27c4e3f085c2e6b8d99e7a79db445e157833
1 # gaf.netlist - gEDA Netlist Extraction and Generation
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2020 Roland Lutz
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # --------------------------------------------------------------------------
22 # SPICE netlist backend written by S. Gieltjes
24 # further modified by W. Kazubski to use scaling parameters for
25 # devices other than MOSFETS
27 # Started with gnet-spice1.scm by W. Kazubski. Radically
28 # hacked by SDB to support advanced spice netlist generation.
29 # Project started 2003-03-05 -- SDB.
31 # Details and documentation at http://www.brorson.com/gEDA/SPICE/
33 # Change log:
34 # 2003-03-05 -- Started hacking. SDB.
35 # 2003-03-17 -- 2nd version. Hacked to allow for .SUBCKT files to model
36 # ics. Changed write-ic. Added get-file-type. Added
37 # write-subcircuit. SDB.
38 # 2003-03-31 -- 3rd version. Hacked to enable creating .SUBCKT schematics
39 # for hierarchical circuit modeling.
40 # 2003-08-29 -- 4th version. Include patches from Ken Healy to sort
41 # netlist, code by SDB to use gnetlist command line args in
42 # Scheme fcns, as well as from Theo Deckers to fix strange
43 # problem with '.SUBCKT quoting.
44 # 2003-09-09 -- 5th version. Rearranged code for more organization (I was
45 # beginning to get lost. . . .). Incorporated changes to
46 # handle external SPICE files more intelligently. Changed
47 # spew to be configurable by setting -v from the command line.
48 # Placed new fcn debug-spew into gnetlist.scm.
49 # Added -I command line flag.
50 # 2003-10-14 -- Bugfixes: Added empty-string? and hacked get-file-type to
51 # handle case where a model file has an empty line before
52 # .SUBCKT or .MODEL.
53 # Also modified write-net-names-on-component to gracefully
54 # handle case where not every pin has a pinseq attribute.
55 # Now only outputs pins with valid pinseq attribute.
56 # 2003-12-25 -- Bugfix: Unswizzled emission of pins from user-defined
57 # .subckts. (Now correctly uses pinseq to define emission
58 # order of pins.) Also added ability to emit attributes for
59 # semiconductors (e.g. area, off, ic, etc.) Added in response
60 # to user requests.
61 # 2003-12-29 -- * Two small enhancements requested by Peter Kaiser.
62 # * Minor bugfix.
63 # * Second minor bugfix.
64 # * Change res & cap to incorporate modelname & "area=" attrib.
65 # 2004-03-24 -- Bugfixes made to JFET stuff during Feb. Change released now.
66 # 2004-08-22 -- Added command line as first line of file.
67 # 2004-08-29 -- Changed sense source naming in controlled sources because
68 # the old convention was confusing ngspice.
69 # 2004-10-09 -- Added patches for voltage controlled switches from
70 # Peter Kaiser.
71 # 2005-03-16 -- Fixed CCCS bug (typo in Vsense) noticed by David Logan
72 # 2005-05-16 -- Modified behavior of .INCLUDE directive. Now by default it
73 # just spits out the string instead of putting the contents of
74 # the file into the SPICE netlist. You can force insertion of
75 # the file using the -e flag.
76 # 2005-06-12 -- Changed order of writing out netlist and .model/.subckt
77 # cards to facilitate use of numparam with ngspice. Change
78 # supplied by Dominique Michel.
79 # 2005-09-11 -- Incorporated patch from Paul Bunyk to enable netlisting of
80 # Josephson junctions and "K" mutual inductances. Also
81 # enabled netlisting of "COIL" devices as inductors.
82 # 2005-12-27 -- Fix bug discovered by John Doty: spice-IO pins with refdes
83 # greater than P9 were sorted incorrectly (as strings). Now
84 # they are sorted as numbers.
85 # 2006-03-10 -- Added "m" attribute to PMOS and NMOS per request of
86 # Peter Kaiser.
87 # 2006-04-11 -- Changed the .END and .ENDS cards to lowercase.
88 # This fixes bug 1442912. Carlos Nieves Onega.
89 # 2007-02-10 -- Various bugfixes. Also incorporated slotted part netlist
90 # patch from Jeff Mallatt. SDB.
91 # 2007-04-28 -- Fixed slotted part stuff so that it uses pinseq to emit
92 # pins. SDB
93 # 2008-01-09 -- Fix slotted part handling to work without a modified pinseq.
94 # pcjc2
95 # 2011-01-03 -- Combine write-ic and write-subcircuit with a fix to the
96 # unbound type variable. Fully document a check for the
97 # special "?" value explaining why it fails silently. Clean
98 # up write-net-names-on-component to make it a bit more
99 # flexible.
100 # Combine write-probe-item and write-net-names-on-component.
101 # Add a range utility function. CC
102 # 2011-01-13 -- Add four lines of code (and some comments) that allow
103 # formaitting strings to be used for netlisting NGspice device
104 # models. CC
105 # 2011-06-12 -- Updated the Problematci name=? symbols to name=unknown and
106 # removed the FIXME check for them. This should be a step
107 # closer to place holder consistancy. CC
108 # 2015-05-14 -- Translated to Python. Roland Lutz
110 #******************************************************************************
112 # Organization of gnet-spice-sdb.scm file:
113 # -- Functions for program housekeeping, handling of calling flags,
114 # file manipulation.
115 # -- Functions for handling nets & devices and creating SPICE cards.
116 # -- High-level functions which control program flow. Note that the
117 # program entry point lives at the very bottom of this file.
119 # Unfortunately, no organization is present beneath this top level. . . .
121 #******************************************************************************
123 import os.path, sys
124 from util_getopt import *
125 from util_repackage import repackage
127 # Common functions for the `spice' and `spice-sdb' backends
128 import spice_common
130 enable_debug = False
132 # Spew debug messages if enable_debug is set, otherwise do nothing.
134 def debug_spew(debug_string):
135 if enable_debug:
136 sys.stderr.write(debug_string)
138 # Custom get-uref function to append ".${SLOT}" where a component has
139 # a "slot=${SLOT}" attribute attached.
141 def get_refdes(component):
142 try:
143 slot = component.blueprint.get_attribute(
144 'slot', search_inherited = False)
145 except KeyError:
146 return component.blueprint.refdes
147 else:
148 return component.blueprint.refdes + '.' + slot
150 ############ Program housekeeping, handling calling flags, etc. ############
152 # Loops through the model-file list, and for each file name discovered
153 # in the list, it processes the file by invoking handle-spice-file.
155 def loop_through_files(f, file_info_list):
156 for list_element in file_info_list:
157 model_name = list_element[0]
158 file_name = list_element[1]
159 file_type = list_element[2]
160 handle_spice_file(f, file_name)
162 # Loops through the model-file list looking for triplet corresponding
163 # to model-name. If found, it returns the corresponding list. If not
164 # found, returns False.
166 def get_file_info_list_item(model_name, file_info_list):
167 for list_element in file_info_list:
168 list_elt_model_name = list_element[0]
169 list_elt_file_name = list_element[1]
170 list_elt_file_type = list_element[2]
172 if list_elt_model_name == model_name:
173 # found model_name. Return list_element.
174 return list_element
176 return None
178 # This wraps insert_text_file.
180 # It looks to see if the -I flag was set at the command line. If so,
181 # it just writes a .INCLUDE card with the file name. If not, it calls
182 # insert_text_file to stick the file's contents into the SPICE netlist.
184 def handle_spice_file(f, file_name):
185 debug_spew("Handling spice model file %s\n" % file_name)
186 if 'include_mode' in calling_flags:
187 # -I found: just print out .INCLUDE card
188 f.write('.INCLUDE %s\n' % file_name)
189 else:
190 # -I not found: invoke insert_text_file
191 insert_text_file(f, file_name)
193 # Given a filename, open the file, get the contents, and dump them
194 # into the spice file.
196 # This function is usually used to include spice models contained in
197 # files into the netlist. Note that it doesn't check the correctness
198 # of the spice code in the file -- you're on your own!
200 def insert_text_file(f, model_filename):
201 if not os.path.isfile(model_filename):
202 sys.stderr.write("ERROR: File '%s' not found.\n" % model_filename)
203 sys.exit(3)
205 model_file = open(model_filename)
206 try:
207 f.write('*vvvvvvvv Included SPICE model from %s vvvvvvvv\n'
208 % model_filename)
210 for model_line in model_file:
211 f.write(model_line)
212 finally:
213 model_file.close()
215 f.write('*^^^^^^^^ End of included SPICE model from %s ^^^^^^^^\n'
216 % model_filename)
217 f.write('*\n')
219 # Figure out if this schematic is a .SUBCKT lower level. This is
220 # determined if there is a spice-subcircuit-LL device instantiated
221 # somewhere on the schematic. If it is a .SUBCKT, return ".SUBCKT
222 # model-name".
224 def get_schematic_type(packages):
225 for package in reversed(packages):
226 if package.get_attribute('device', None) == 'spice-subcircuit-LL':
227 # look for subcircuit label
228 return '.SUBCKT ' + package.get_attribute('model-name', 'unknown')
230 # no spice-subcircuit-LL found
231 return 'normal schematic'
233 # Extract the modelname from the .SUBCKT modelname line.
234 # Just grab the chars from char 8 to the end of the string.
236 def get_subcircuit_modelname(schematic_type):
237 return schematic_type[8:]
239 # This iterates through the schematic and compiles a list of all
240 # spice-IO pins found. This is used when writing out a .SUBCKT lower
241 # level netlist.
243 def get_spice_IO_pins(packages):
244 spice_io_package_list = []
245 for package in reversed(packages):
246 # look for subcircuit label
247 if package.get_attribute('device', None) == 'spice-IO':
248 # we have found a spice-IO pin.
249 spice_io_package_list.append(package)
251 # end iteration & return list
252 spice_io_package_list.reverse()
253 return spice_io_package_list
255 # This takes the list of io-pin-packages and sorts it in order of
256 # refdes. Repaired on 2005-12-27 to correctly sort pin numbers > 9.
258 def sort_spice_IO_pins(package_list):
259 return sort(package_list, key = lambda x: int(x.refdes[1:]))
261 # Given a list of spice-IO packages (refdeses), this function returns
262 # the list of nets attached to the IOs.
264 def get_IO_nets(package_list):
265 net_list = []
266 for package in package_list:
267 # get the net attached to pin 1
268 net_list.append(package.pins_by_number['1'].net.name)
270 # end iteration & return net_list
271 net_list.reverse()
272 return net_list
274 # Given a filename, open the file, get the first line, and see if it
275 # is a .MODEL or .SUBCKT file. Returns either ".MODEL" or ".SUBCKT"
276 # or "OTHER". The function opens the model file, and closes it when
277 # it is done.
279 def get_file_type(model_filename):
280 if not os.path.isfile(model_filename):
281 sys.stderr.write("ERROR: File '%s' not found.\n" % model_filename)
282 sys.exit(3)
284 model_file = open(model_filename)
285 try:
286 for file_line in model_file:
287 if file_line.startswith('.'):
288 debug_spew("In get_file_type, first_char = .\n")
289 if file_line[:7].lower() == '.subckt':
290 # found .subckt as first line.
291 return '.SUBCKT'
292 if file_line[:6].lower() == '.model':
293 # found .model as first line.
294 return '.MODEL'
295 # first . spice card is neither .model nor .subckt
296 return 'OTHER'
298 # Arrived at end of line without finding .MODEL or .SUBCKT.
299 return 'OTHER'
300 finally:
301 model_file.close()
303 # Write prefix if first char of refdes is improper, e.g. if MOSFET is
304 # named T1 then becomes MT1 in SPICE.
306 def write_prefix(f, package, prefix):
307 different_prefix = package.refdes[0] != prefix
308 nomunge = 'nomunge_mode' in calling_flags
310 debug_spew("Checking prefix. Package prefix =%s\n" % package.refdes[0])
311 debug_spew(" correct prefix =%s\n" % prefix)
312 debug_spew(" nomunge mode = %s\n different_prefix=%s\n"
313 % (nomunge, different_prefix))
314 if different_prefix and not nomunge:
315 f.write(prefix)
317 # Sort procedure to order refdes's alphabetically but keep A? packages
318 # at the end of list so SPICE simulation directives operate correctly.
320 # This fcn written by Ken Healy to enable SPICE netlisting for Gnucap,
321 # which wants A refdes cards (i.e. SPICE directives) to appear last in
322 # the SPICE netlist. Slightly modified and incorporated into main
323 # spice-sdb release by SDB on 2003-09-01. To output the netlist in
324 # sorted order, use the -s switch when invoking gnetlist from the
325 # command line.
327 # Example: gnetlist -s -g spice-sdb -o output.spice schematic.sch
329 # The default behavior (i.e. if -s is not specified) is to do no
330 # sorting.
332 def packsort(x, y):
333 xdes = x.refdes[0].lower()
334 ydes = y.refdes[0].lower()
335 xnum = x.refdes[1:].lower()
336 ynum = y.refdes[1:].lower()
338 if xdes == ydes:
339 if xnum < ynum:
340 return -1
341 if xnum > ynum:
342 return 1
343 return 0
344 if xdes == 'a': # and ydes != 'a'
345 return 1
346 if ydes == 'a': # and xdes != 'a'
347 return -1
348 if xdes < ydes:
349 return -1
350 if xdes > ydes:
351 return 1
352 return 0
355 ################ Dealing with nets, devices, & SPICE cards. ################
357 # Write-transistor-diode: writes out component followed by model or
358 # model file associated with the component.
359 # This function does the following:
360 # 1. Writes out the correct refdes prefix (if specified and necessary).
361 # 2. Writes out the refdes and nets
362 # 3. Looks for "model-name" attribute. Writes it out if it exists.
363 # 4. If there is no "model-name" attribute, it writes out the "value"
364 # attribute. If there is no "value" attribute, it writes out "unknown"
365 # and returns, causing the spice simulator to puke when the netlist
366 # is run. This is important
367 # 'cause the spice simulator needs to have some indication of what
368 # model to look for.
369 # 5. Outputs optional attributes attached to device, if any. Feature
370 # added by SDB on 2003-12-25.
371 # 6. Outputs a new line
372 # 7. Looks for a the "model" attribute. If it exists, it it writes out
373 # a .MODEL line like this: .MODEL model-name type (model)
375 def write_transistor_diode(f, package, prefix, type, attrib_list):
376 # First do local assignments
377 model_name = package.get_attribute('model-name', None)
378 model = package.get_attribute('model', None)
379 value = package.get_attribute('value', None)
380 area = package.get_attribute('area', None)
381 off = package.get_attribute('off', None)
382 model_file = package.get_attribute('file', None)
384 # Write out the refdes prefix, if specified and necessary.
385 if prefix:
386 write_prefix(f, package, prefix)
388 # Next we write out the refdes and nets.
389 write_component_no_value(f, package)
391 # next look for "model-name" attribute. Write it out if it exists.
392 # otherwise look for "value" attribute.
393 if model_name is not None:
394 # display model-name if known
395 f.write(model_name + ' ')
396 elif value is not None:
397 # otherwise display value
398 f.write(value + ' ')
399 else:
400 f.write('unknown ')
402 # Next write out attributes if they exist
403 # First attribute is area. It is written as a simple string
404 if area is not None:
405 f.write(area + ' ')
407 # Next attribute is off. It is written as a simple string
408 if off is not None:
409 f.write(off + ' ')
411 # Write out remaining attributes
412 spice_common.write_list_of_attributes(f, package, attrib_list)
414 # Now write out newline in preparation for writing out model.
415 f.write('\n')
417 # Now write out any model which is pointed to by the part.
418 # one line model and model name exist
419 if model is not None and model_name is not None:
420 debug_spew("found model and model-name for %s\n" % package.refdes)
421 f.write('.MODEL %s %s (%s)\n' % (model_name, type, model))
422 # one line model and component value exist
423 elif model_name is not None and value is not None:
424 debug_spew("found model and value for %s\n" % package.refdes)
425 f.write('.MODEL %s %s (%s)\n' % (model_name, type, value))
426 # model file and model name exist
427 elif model_file is not None and model_name is not None:
428 debug_spew("found file and model-name for %s\n" % package.refdes)
429 debug_spew("I'll deal with the file later . . .\n")
430 # model file and component value exist
431 elif model_file is not None and value is not None:
432 debug_spew("found file and value for %s\n" % package.refdes)
433 debug_spew("I'll deal with the file later . . .\n")
435 # This writes out a valid diode refdes & then calls the function which
436 # writes the rest of the line.
438 def write_diode(f, package):
439 debug_spew("Found diode. Refdes = %s\n" % package.refdes)
440 write_transistor_diode(f, package, 'D', 'D', ['ic', 'temp'])
442 # This writes out a valid ic or subcircuit line.
443 # The algorithm is as follows:
444 # 1. Figure out what type of model goes with this part from
445 # file-info-list. If it isn't listed, look for a MODEL attribute.
446 # If MODEL attribute is attached, write out SPICE card, and then
447 # write out .MODEL on next line.
448 # If no MODEL attribute is attached, just write out what little
449 # we know. Then return
450 # 2. If the model-name is in the file-info-list, get the associated
451 # file-type. Compare it against the component's refdes. If model-type
452 # is .MODEL or .SUBCKT and refdes doesn't begin with a U or X
453 # respectively, prepend the correct prefix to the refdes.
454 # 3. Print out the rest of the line.
456 def write_ic(f, package, file_info_list):
457 # First do local assignments
458 first_char = package.refdes[0] # extract first char of refdes
459 value = package.get_attribute('value', 'unknown')
460 # If model-name is empty, we use value attribute instead.
461 model_name = package.get_attribute('model-name', value)
462 model = package.get_attribute('model', None)
463 type = package.get_attribute('type', None)
465 if first_char == 'U':
466 debug_spew("Found ic. Refdes = %s\n" % package.refdes)
467 elif first_char == 'X':
468 debug_spew("Found subcircuit. Refdes = %s\n" % package.refdes)
470 # Now get item from file_info_list using model_name as key
471 list_item = get_file_info_list_item(model_name, file_info_list)
473 # check to see if list_item is null.
474 if list_item is None:
475 # Evidently, we didn't discover any files holding this model.
476 # Instead we look for model attribute
477 if model is not None:
478 # model attribute exists -- write out card and model.
479 debug_spew("Model info not found in model file list, "
480 "but model attribute exists. "
481 "Write out spice card and .model line..\n")
482 write_component_no_value(f, package)
483 f.write(model_name + '\n')
484 f.write('.MODEL %s ' % model_name)
485 if type is not None:
486 f.write(type + ' ')
487 # If no type then just skip it.
488 f.write('(%s)\n' % model)
489 else:
490 # no model attribute either. Just write out card.
491 debug_spew("Model info not found in model file list. No "
492 "model attribute either. Just write what we know.\n")
493 write_component_no_value(f, package)
494 f.write(model_name + '\n')
496 else:
497 # list_item is not null. Therefore we process line depending
498 # upon contents of list_item
499 file_type = list_item[2]
501 if file_type == '.MODEL':
502 # ---- file holds a model ----
503 debug_spew("Found .MODEL with model-file and model-name for %s\n"
504 % package.refdes)
505 write_prefix(f, package, 'U')
506 # this prepends an "U" to the refdes if needed, since we
507 # have a .model
508 write_component_no_value(f, package)
509 f.write(model_name + '\n')
510 debug_spew("We'll handle the file contents later . . .\n")
512 elif file_type == '.SUBCKT':
513 # ---- file holds a subcircuit ----
514 debug_spew("Found .SUBCKT with model-file and model-name for %s\n"
515 % package.refdes)
516 write_prefix(f, package, 'X')
517 # this prepends an "X" to the refdes if needed, since we
518 # have a .subckt
519 write_component_no_value(f, package)
520 f.write(model_name + '\n')
521 debug_spew("We'll handle the file contents later . . .\n")
523 # This writes out a valid transistor refdes & then calls the function
524 # which writes the rest of the line.
526 def write_npn_bipolar_transistor(f, package):
527 debug_spew("Found npn bipolar transistor. Refdes = %s\n" % package.refdes)
528 write_transistor_diode(f, package, 'Q', 'NPN', ['ic', 'temp'])
530 def write_pnp_bipolar_transistor(f, package):
531 debug_spew("Found pnp bipolar transistor. Refdes = %s\n" % package.refdes)
532 write_transistor_diode(f, package, 'Q', 'PNP', ['ic', 'temp'])
534 # Write n-channel JFET transistor.
536 def write_nfet_transistor(f, package):
537 debug_spew("Found n-channel JFET. Refdes = %s\n" % package.refdes)
538 write_transistor_diode(f, package, 'J', 'NJF', ['ic', 'temp'])
540 # Write p-channel JFET transistor.
542 def write_pfet_transistor(f, package):
543 debug_spew("Found p-channel JFET. Refdes = %s\n" % package.refdes)
544 write_transistor_diode(f, package, 'J', 'PJF', ['ic', 'temp'])
546 def write_pmos_transistor(f, package):
547 debug_spew("Found PMOS transistor. Refdes = %s\n" % package.refdes)
548 write_transistor_diode(f, package, 'M', 'PMOS', [
549 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
551 def write_nmos_transistor(f, package):
552 debug_spew("Found NMOS transistor. Refdes = %s\n" % package.refdes)
553 write_transistor_diode(f, package, 'M', 'NMOS', [
554 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
556 def write_subckt_pmos_transistor(f, package):
557 debug_spew("Found PMOS subcircuit transistor. "
558 "Refdes = %s\n" % package.refdes)
559 write_transistor_diode(f, package, 'X', 'PMOS', [
560 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
562 def write_subckt_nmos_transistor(f, package):
563 debug_spew("Found NMOS subcircuit transistor. "
564 "Refdes = %s\n" % package.refdes)
565 write_transistor_diode(f, package, 'X', 'NMOS', [
566 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
568 # ************ Fix this!!!!!!!!!! **************
569 def write_mesfet_transistor(f, package):
570 write_transistor_diode(f, package, 'Z', 'MESFET', [])
571 # XXXXXX Fix this!!!
573 # Write voltage controlled switch.
575 def write_vc_switch(f, package):
576 debug_spew("Found voltage controlled switch. "
577 "Refdes = %s\n" % package.refdes)
578 write_transistor_diode(f, package, 'S', 'SW', [' '])
580 def write_resistor(f, package):
581 debug_spew("Found resistor. Refdes = %s\n" % package.refdes)
583 # first write out refdes and attached nets
584 write_component_no_value(f, package)
586 # next write out mandatory resistor value if it exists.
587 value = package.get_attribute('value', None)
588 if value is not None:
589 f.write(value + ' ')
591 # next write our model name if it exists
592 model_name = package.get_attribute('model-name', None)
593 if model_name is not None:
594 f.write(model_name + ' ')
596 # write the attributes (if any) separately
597 # I include non-standard "area" attrib here per popular demand
598 # in the list of attributes which can be attached to a resistor.
599 spice_common.write_list_of_attributes(
600 f, package, ['area', 'l', 'w', 'temp'])
601 # add additional space. . . .
602 f.write(' ')
604 # finally output a new line
605 f.write('\n')
607 def write_capacitor(f, package):
608 debug_spew("Found capacitor. Refdes = %s\n" % package.refdes)
610 # first write out refdes and attached nets
611 write_component_no_value(f, package)
613 # next write capacitor value, if any. Note that if the
614 # component value is not assigned nothing will be written out.
615 value = package.get_attribute('value', None)
616 if value is not None:
617 f.write(value + ' ')
619 # next write capacitor model name, if any. This is applicable to
620 # semiconductor caps used in chip design.
621 model_name = package.get_attribute('model-name', None)
622 if model_name is not None:
623 f.write(model_name + ' ')
625 # Next write out attributes if they exist. Use
626 # a list of attributes which can be attached to a capacitor.
627 # I include non-standard "area" attrib here per request of Peter Kaiser.
628 spice_common.write_list_of_attributes(
629 f, package, ['area', 'l', 'w', 'ic'])
630 # write the off attribute separately
631 # add additional space. . . .
632 f.write(' ')
634 f.write('\n')
636 def write_inductor(f, package):
637 debug_spew("Found inductor. Refdes = %s\n" % package.refdes)
639 # first write out refdes and attached nets
640 write_component_no_value(f, package)
642 # next write inductor value, if any. Note that if the
643 # component value is not assigned, then it will write "unknown"
644 f.write(package.get_attribute('value', 'unknown'))
646 spice_common.write_list_of_attributes(
647 f, package, ['l', 'w', 'ic'])
648 # write the off attribute separately
649 # add additional space. . . .
650 f.write(' ')
652 f.write('\n')
654 # The behavior of the voltage source is held in the "value" attribute.
656 def write_independent_voltage_source(f, package):
657 debug_spew("Found independent voltage source. "
658 "Refdes = %s\n" % package.refdes)
660 # first write out refdes and attached nets
661 write_component_no_value(f, package)
663 # next write voltage value, if any. Note that if the
664 # voltage value is not assigned, then it will write "unknown"
665 f.write(package.get_attribute('value', 'unknown'))
667 f.write('\n')
669 # The behavior of the current source is held in the "value" attribute.
671 def write_independent_current_source(f, package):
672 debug_spew("Found independent current source. "
673 "Refdes = %s\n" % package.refdes)
675 # first write out refdes and attached nets
676 write_component_no_value(f, package)
678 # next write current value, if any. Note that if the
679 # current value is not assigned, then it will write "unknown"
680 f.write(package.get_attribute('value', 'unknown'))
682 f.write('\n')
684 # Write Josephson junction in wrspice format. Paul Bunyk, Sep 2, 2005
686 def write_josephson_junction(f, package):
687 debug_spew("Found Josephson junction. Refdes = %s\n" % package.refdes)
689 # first write out refdes and attached nets
690 write_component_no_value(f, package)
692 # next, add a dummy node for JJ phase. Unlike in Xic netlister, give it
693 # a reasonable name, not a number, e.g., refdes.
694 f.write(package.refdes + ' ')
696 # next write JJ model name, if any.
697 model_name = package.get_attribute('model-name', None)
698 if model_name is not None:
699 f.write(model_name + ' ')
701 # Next write out attribtes if they exist. Use
702 # a list of attributes which can be attached to a junction.
703 spice_common.write_list_of_attributes(f, package, ['area'])
704 # write the off attribute separately
705 # add additional space. . . .
706 f.write(' ')
708 f.write('\n')
710 # Write mutual inductance (actually K). Paul Bunyk, Sep 2, 2005
712 def write_coupling_coefficient(f, package):
713 debug_spew("Found mutual inductance. Refdes = %s\n" % package.refdes)
715 # first write out refdes and attached nets (none)
716 write_component_no_value(f, package)
718 # next two inductor names and value
719 inductors = package.get_attribute('inductors', None)
720 value = package.get_attribute('value', None)
721 if inductors is not None:
722 f.write(inductors + ' ')
723 if value is not None:
724 f.write(value + ' ')
726 f.write('\n')
728 # Write a voltage probe.
730 def write_probe(f, package):
731 # fetch only one attr we care about, so far
732 value = package.get_attribute('value', 'TRAN')
734 debug_spew("Found Probe item, refdes = %s\n" % package.refdes)
736 f.write('* Probe device %s on nets ' % packager.refdes)
737 write_net_names_on_component(f, package)
738 f.write('\n')
739 f.write('.print %s +' % value)
740 # make format string
741 fmt = ' '.join(['V(%s)'] * len(package.pins))
742 write_net_names_on_component(f, package, fmt)
743 f.write('\n')
745 # Given a refdes, and optionally a format string, this writes out the
746 # nets attached to the component's pins. If it's not called with a
747 # format string it looks for one in the net-format attribute,
748 # otherwise it writes out the pins unformatted. This is used to write
749 # out non-slotted parts.
751 def write_net_names_on_component(f, package, format = None):
752 netnames = []
753 for i in range(1, len(package.pins) + 1):
754 # Get net name for pinseq i and add it to netnames,
755 # unless it is "ERROR_INVALID_PIN".
757 # ------- Super debug stuff --------
758 if False:
759 debug_spew(" In write_net_names_on_component. . . . \n")
760 debug_spew(" pin-name = %s\n" % str(i))
761 debug_spew(" pinnumber = %s\n" %
762 package.get_pin_by_pinseq(i).get_attribute('pinnumber', 'unknown'))
763 debug_spew(" pinseq = %s" %
764 package.get_pin_by_pinseq(i).get_attribute('pinseq', 'unknown'))
765 if str(i) != package.get_pin_by_pinseq(i).get_attribute('pinseq', 'unknown'):
766 debug_spew(" <== INCONSISTENT!\n")
767 else:
768 debug_spew("\n")
769 debug_spew(" netname = %s\n",
770 spice_common.get_net_name(package,
771 package.get_pin_by_pinseq(i).get_attribute('pinnumber', 'unknown')))
772 # -------------------------------------
774 try:
775 pin = package.get_pin_by_pinseq(i)
776 except KeyError:
777 debug_spew("For %s, found pin with no pinseq attribute. "
778 "Ignoring. . . .\n" % package.refdes)
779 else:
780 netnames.append(spice_common.get_net_name(package, pin.number))
782 # Format agument take priority, otherwise use attribute
783 if format is None:
784 format = package.get_attribute('net-format', None)
786 if format is None:
787 # write out nets.
788 f.write(''.join(netname + ' ' for netname in netnames))
789 else:
790 # write out nets with format string
791 f.write(format % netnames)
793 # Write the refdes and the net names connected to pins on this
794 # component. No return, and no component value is written, or extra
795 # attribs. Those are handled later.
797 def write_component_no_value(f, package):
798 f.write(package.refdes + ' ')
799 # write component refdes
800 write_net_names_on_component(f, package)
802 # Given a refdes, returns the device attribute "value" as string.
803 # Used when "value" is an optional attribute. Returns "unknown" if
804 # not available.
806 def component_optional_value(package):
807 value = package.get_attribute('value', None)
808 if value is None:
809 return ''
810 return value + ' '
812 # Given a refdes, returns the device attribute "model" as string.
814 def component_model(package):
815 # This returns either a string or a function!?
816 model = package.get_attribute('model', None)
817 if model is None:
818 return spice_common.component_value # TODO: looks wrong
819 return model
821 # Include SPICE statements from a SPICE directive block.
823 def write_directive(f, package):
824 # Collect variables used in creating spice code
825 value = package.get_attribute('value', None)
826 file = package.get_attribute('file', None)
828 debug_spew("Found SPICE directive box. Refdes = %s\n" % package.refdes)
830 # First look to see if there is a value.
831 if value is not None:
832 f.write(value + '\n')
833 debug_spew("Appending value = \"%s\" to output file.\n" % value)
834 # since there is no value, look for file.
835 elif file is not None:
836 insert_text_file(f, file)
837 # Note that we don't wait until the end here. Is that OK?
838 debug_spew("Inserting contents of file = %s into output file.\n" % file)
840 # Include a file using an .INCLUDE directive
841 # Changed on 2005-06-12: to embed the contents of the file,
842 # you must call gnetlist with the -e flag set.
844 def write_include(f, package):
845 file = package.get_attribute('file', None)
846 debug_spew("Found SPICE include box. Refdes = %s\n" % package.refdes)
848 if file is None:
849 debug_spew("silently skip \"unknown\" file.\n")
850 return
852 if 'embedd_mode' in calling_flags:
853 # -e found: invoke insert_text_file
854 insert_text_file(f, file)
855 debug_spew("embedding contents of file %s into netlist.\n" % file)
856 else:
857 # -e not found: just print out .INCLUDE card
858 f.write('.INCLUDE %s\n' % file)
859 debug_spew("placing .include directive string into netlist.\n")
861 # Include an option using an .OPTIONS directive.
863 def write_options(f, package):
864 debug_spew("Found .OPTIONS box. Refdes = %s\n" % package.refdes)
865 f.write('.OPTIONS %s\n' % spice_common.component_value(package))
867 # Include a spice model (instantiated as a model box on the schematic).
868 # Two types of model can be included:
869 # 1. An embedded model, which is a one- or multi-line string held in
870 # the attribute "model".
871 # In this case, the following attributes are mandatory:
872 # -- model (i.e. list of parameter=value strings)
873 # -- model-name
874 # -- type
875 # In this case, the function creates and formats the correct
876 # spice model line(s).
877 # 2. A model held in a file whose name is held in the attribute "file"
878 # In this case, the following attribute are mandatory:
879 # -- file (i.e. list of parameter=value strings)
880 # In this case, the function just opens the file and dumps the
881 # contents into the netlist.
883 def write_model(f, package):
884 # Collect variables used in creating spice code
885 model_name = package.get_attribute('model-name', None)
886 model_file = package.get_attribute('file', None)
887 model = package.get_attribute('model', None)
889 debug_spew("Found .MODEL box. Refdes = %s\n" % package.refdes)
891 # Now, depending upon what combination of model, model_file, and
892 # model_name exist (as described above) write out lines into spice
893 # netlist.
894 if model is not None and model_name is not None:
895 # one model and model name exist
896 debug_spew("found model and model_name for %s\n" % package.refdes)
897 f.write('.MODEL %s %s (%s)\n' % (
898 model_name, package.get_attribute('type', 'unknown'), model))
899 elif model_file is not None:
900 # model file exists
901 debug_spew("found model_file for %s\n" % package.refdes)
902 # insert_text_file(f, model_file)
903 # don't write it out -- it's handled after the second pass.
905 # This writes out the default component (i.e. the "device" attribute
906 # was not recognized). This function does the following:
908 # 1. Gets the refdes (package).
909 # 2. Checks the refdes against a short list of possible values.
910 # Depending upon the refdes, it does the following thing:
911 # A? -- Invokes write-ic. This provides the opportunity for a
912 # code model which may include a .model line.
913 # D? -- Invokes write-diode
914 # Q? -- Invokes write-transistor-diode. (The "type" attribute is
915 # <unknown> in this case so that the spice simulator will
916 # barf if the user has been careless.)
917 # M? -- Same as Q
918 # U? -- Invokes write-ic. This provides the opportunity for a
919 # component model to be instantiated.
920 # X? -- Invokes write-ic. This provides the opportunity for a
921 # component subcircuit to be instantiated.
922 # V? -- Invokes write-independent-voltage-source
923 # I? -- Invokes write-independent-current-source
924 # Otherwise, it just outputs the refdes, the attached nets, and
925 # the value of the "value" attribute.
927 def write_default_component(f, package, file_info_list):
928 # extract first char of refdes.
929 first_char = package.refdes[0]
931 if first_char == 'A':
932 write_ic(f, package, file_info_list)
933 elif first_char == 'D':
934 write_diode(f, package)
935 elif first_char == 'Q' or first_char == 'M':
936 write_transistor_diode(f, package, False, '<unknown>', [])
937 elif first_char == 'U':
938 write_ic(f, package, file_info_list)
939 elif first_char == 'V':
940 write_independent_voltage_source(f, package)
941 elif first_char == 'I':
942 write_independent_current_source(f, package)
943 elif first_char == 'X':
944 write_ic(f, package, file_info_list)
945 else:
946 package.warn("unknown component")
947 write_component_no_value(f, package)
948 # write component value, if components have a label "value=#"
949 # what if a component has no value label, currently unknown is written
950 f.write(spice_common.component_value(package))
951 f.write('\n')
954 ################# High-level functions for program control #################
956 # This function is passed a list of refdesses (ls). It uses each
957 # refdes to get the corresponding "device" attribute. Depending upon
958 # the device, it then invokes one or another of the spice line output
959 # fcns to output a line of the spice netlist. I have enlarged the
960 # number of devices it recognizes -- SDB.
961 # Write the refdes, to the pin# connected net and component value and
962 # optional extra attributes. Check if the component is a special
963 # spice component.
965 def write_netlist(f, file_info_list, ls):
966 for package in ls:
967 device = package.get_attribute('device', 'unknown')
969 # Super debug stuff -- outputs line describing device being processed.
970 debug_spew("--- checking package = %s\n" % package.refdes)
971 debug_spew(" device = %s\n" % device)
973 if device == 'none':
974 pass # do nothing for graphical symbols.
975 elif device == 'spice-subcircuit-LL':
976 pass # do nothing for subcircuit declaration.
977 elif device == 'spice-IO':
978 pass # do nothing for SPICE IO pins.
979 elif device == 'spice-title':
980 pass # do nothing for spice title blocks
981 elif device == 'SPICE-ccvs':
982 spice_common.write_ccvs(f, package)
983 elif device == 'SPICE-cccs':
984 spice_common.write_cccs(f, package)
985 elif device == 'SPICE-vcvs':
986 spice_common.write_vcvs(f, package)
987 elif device == 'SPICE-vccs':
988 spice_common.write_vccs(f, package)
989 elif device == 'SPICE-nullor':
990 spice_common.write_nullor(f, package)
991 elif device == 'DIODE':
992 write_diode(f, package)
993 elif device == 'PMOS_TRANSISTOR':
994 write_pmos_transistor(f, package)
995 elif device == 'NMOS_TRANSISTOR':
996 write_nmos_transistor(f, package)
997 elif device == 'PNP_TRANSISTOR':
998 write_pnp_bipolar_transistor(f, package)
999 elif device == 'SPICE-PNP':
1000 write_pnp_bipolar_transistor(f, package)
1001 elif device == 'NPN_TRANSISTOR':
1002 write_npn_bipolar_transistor(f, package)
1003 elif device == 'SPICE-NPN':
1004 write_npn_bipolar_transistor(f, package)
1005 elif device == 'PFET_TRANSISTOR':
1006 write_pfet_transistor(f, package)
1007 elif device == 'NFET_TRANSISTOR':
1008 write_nfet_transistor(f, package)
1009 elif device == 'MESFET_TRANSISTOR':
1010 write_mesfet_transistor(f, package)
1011 elif device == 'SPICE-VC-switch':
1012 write_vc_switch(f, package)
1013 elif device == 'RESISTOR':
1014 write_resistor(f, package)
1015 elif device == 'CAPACITOR':
1016 write_capacitor(f, package)
1017 elif device == 'POLARIZED_CAPACITOR':
1018 write_capacitor(f, package) # change someday
1019 elif device == 'INDUCTOR':
1020 write_inductor(f, package)
1021 elif device == 'COIL':
1022 # Added to enable netlisting of coil-*.sym
1023 write_inductor(f, package)
1024 elif device == 'VOLTAGE_SOURCE':
1025 write_independent_voltage_source(f, package)
1026 # change someday
1027 elif device == 'CURRENT_SOURCE':
1028 write_independent_current_source(f, package)
1029 # change someday
1030 elif device == 'JOSEPHSON_JUNCTION':
1031 write_josephson_junction(f, package)
1032 elif device == 'K':
1033 write_coupling_coefficient(f, package)
1034 elif device == 'model':
1035 write_model(f, package)
1036 elif device == 'options':
1037 write_options(f, package)
1038 elif device == 'directive':
1039 write_directive(f, package)
1040 elif device == 'include':
1041 write_include(f, package)
1042 elif device == 'TESTPOINT':
1043 write_probe(f, package)
1044 elif device == 'SUBCKT_PMOS':
1045 write_subckt_pmos_transistor(f, package)
1046 elif device == 'SUBCKT_NMOS':
1047 write_subckt_nmos_transistor(f, package)
1048 else:
1049 write_default_component(f, package, file_info_list)
1051 # This runs through the list of packages (refdesses), and for each
1052 # gets the attributes. If there is a "FILE" attribute, it gets the
1053 # file info & uses it to build the file-info-list. When done, it
1054 # returns the file_info_list.
1056 def create_file_info_list(packages):
1057 file_info_list = []
1058 for package in reversed(packages):
1059 model = package.get_attribute('model-name', 'unknown')
1060 model_file = package.get_attribute('file', None)
1062 # Now run a series of checks to see if we should stick this
1063 # file into the file_info_list
1065 # Check to see if "file" attribute is non-empty
1066 if model_file is None:
1067 continue
1069 debug_spew("found file attribute for %s. File name = %s\n"
1070 % (package.refdes, model_file))
1072 # Now check to see if file is in file_info_list
1073 if is_in_file_info_list(model_file, file_info_list):
1074 # File is already in list. Print debug spew if desired.
1075 debug_spew("File has already been seen and entered into "
1076 "known model file list.\n")
1077 continue
1079 # File is new. Open file, find out what type it is, and push
1080 # info into file_info_list
1081 file_type = get_file_type(model_file)
1082 debug_spew("File is new. New file type is %s \n" % file_type)
1084 # Check to see if file_type is known.
1085 if file_type == 'OTHER':
1086 # File type is not a model type. Don't stick it in list.
1087 # Print debug spew if desired.
1088 debug_spew("File type is OTHER, and therefore will not be "
1089 "entered in known model file list.\n")
1090 continue
1092 # file_type is OK. Return file_info_list with new triplet attached.
1093 debug_spew("Inserting %s into list of known model files.\n"
1094 % model_file)
1095 file_info_list.append((model, model_file, file_type))
1097 # end of packages processed. Return file_info_list
1098 file_info_list.reverse()
1099 return file_info_list
1101 # Helper function. Returns True if file is already in file_info_list,
1102 # otherwise False. Assumes file_info_list of form:
1103 # [(model1, file1, file_type1), (model2, file2, file_type2), ...]
1105 def is_in_file_info_list(model_file, file_info_list):
1106 for list_element in file_info_list:
1107 list_file_name = list_element[1]
1108 if list_file_name == model_file:
1109 return True # item found
1111 return False
1114 # Write out spice netlist header
1116 def write_top_header(f):
1117 f.write('*********************************************************\n')
1118 f.write('* Spice file generated by gnetlist *\n')
1119 f.write('* spice-sdb version 4.28.2007 by SDB -- *\n')
1120 f.write('* provides advanced spice netlisting capability. *\n')
1121 f.write('* Documentation at http://www.brorson.com/gEDA/SPICE/ *\n')
1122 f.write('*********************************************************\n')
1124 # Write out .SUBCKT netlist header
1126 def write_subcircuit_header(f):
1127 f.write('*******************************\n')
1128 f.write('* Begin .SUBCKT model *\n')
1129 f.write('* spice-sdb ver 4.28.2007 *\n')
1130 f.write('*******************************\n')
1132 # Spice netlist generation.
1133 # This is the entry point.
1135 # Hacked on 2003-03-31 to enable writing out .SUBCKT models -- SDB.
1136 # Hacked again in Sept 2003 to enable more intelligent embedding of
1137 # external SPICE files into netlist -- SDB.
1138 # The algorithm is as follows:
1139 # 1. Figure out if there is a .SUBCKT block on the schematic,
1140 # or if it is just a normal schematic.
1141 # If a .SUBCKT:
1142 # -- Write out subcircuit header (a comment identifying the netlister).
1143 # -- find all spice-IO pins. Get a list of the packages.
1144 # -- put them in order (ordered by package refdes)
1145 # -- get the list of nets attached to the spice-IO pins.
1146 # -- write out .SUBCKT line
1147 # If a normal schematic:
1148 # -- Write out top header (a comment identifying the netlister).
1149 # 2. Loop through all components, looking for components with a "file"
1150 # attribute. Every time a "file" attribute is found do this:
1151 # -- Open the file and find out what kind of file it is (.SUBCKT
1152 # or .MODEL).
1153 # -- Determine if the file has previously been processed. If
1154 # not: stick the following info into the file-info list:
1155 # (model_name, file_name, file_type). Otherwise just continue.
1156 # 3. Loop through all components again, and write out a SPICE card for each.
1157 # 4. Afterwards, for each item in the file-info list, open the file,
1158 # and write its contents into the netlist.
1159 # 5. If the schematic-type is .SUBCKT: write out .ENDS, otherwise
1160 # write out .END
1161 # 6. Close up the SPICE netlist file and return.
1163 def run(f, netlist, args):
1164 # Parse backend options passed via the `-O' command-line option.
1165 global calling_flags
1166 calling_flags = backend_getopt(args, {
1167 'include_mode': Option(False, NO_ARGUMENT, None),
1168 'nomunge_mode': Option(False, NO_ARGUMENT, None),
1169 'sort_mode': Option(False, NO_ARGUMENT, None),
1170 'embedd_mode': Option(False, NO_ARGUMENT, None),
1171 'no_end_card': Option(False, NO_ARGUMENT, None)
1174 # Redefine write_net_names_on_component
1175 spice_common.write_net_names_on_component = write_net_names_on_component
1177 # Re-group components into packages using a custom refdes function
1178 # in order to treat slots as individual packages
1179 packages = repackage(netlist, get_refdes)
1181 # First find out if this is a .SUBCKT lower level,
1182 # or if it is a regular schematic.
1184 schematic_type = get_schematic_type(packages)
1185 model_name = get_subcircuit_modelname(schematic_type)
1187 sys.stderr.write("Using SPICE backend by SDB -- Version of 2007-04-28\n")
1188 sys.stderr.write("schematic_type = %s\n" % schematic_type)
1190 if schematic_type != 'normal schematic':
1191 # we have found a .SUBCKT type schematic.
1192 io_pin_packages = get_spice_IO_pins(packages)
1193 io_pin_packages_ordered = sort_spice_IO_pins(io_pin_packages)
1194 io_nets_list = get_IO_nets(io_pin_packages_ordered)
1195 debug_spew("found .SUBCKT type schematic\n")
1197 # now write out .SUBCKT header and .SUBCKT line
1198 write_subcircuit_header(f)
1199 f.write('%s %s \n' % (schematic_type,
1200 ' '.join(reversed(io_nets_list))))
1201 else:
1202 # Otherwise it's a regular schematic. Write out command line
1203 # followed by comments in file header.
1204 debug_spew("found normal type schematic\n")
1206 # If the schematic contains a spice-title device and the value
1207 # attribute is a string, use that as the title of the spice netlist
1208 for package in reversed(packages):
1209 if package.get_attribute('device', None) == 'spice-title':
1210 title = package.get_attribute('value', None)
1211 if title is None:
1212 continue
1213 f.write(title + '\n')
1214 break
1216 f.write('* %s\n' % ' '.join(sys.argv))
1217 write_top_header(f)
1219 # Now loop through all devices and process all "FILE" attributes. Create
1220 # file_info_list.
1221 # Thanks to Carlos Nieves Onega for his e-mail to
1222 # geda-dev which is the genesis of this section.
1224 debug_spew("Make first pass through design and "
1225 "create list of all model files referenced.\n")
1226 file_info_list = create_file_info_list(packages)
1227 debug_spew("Done creating file_info_list.\n\n")
1229 # Moved this loop before the next one to get numparam to work with
1230 # ngspice, because numparam will at the subckt definition come
1231 # before the main netlist. Change suggested by Dominique Michel;
1232 # implemented in code on 2005-06-12.
1234 # Next loop through all items in file_info_list in the SPICE
1235 # netlist. For each model_name, open up the corresponding file,
1236 # and call handle_spice_file to stick the corresponding stuff into
1237 # the output SPICE file.
1239 debug_spew("Now process the items in model file list -- "
1240 "stick appropriate references to models in output SPICE file.\n")
1241 loop_through_files(f, file_info_list)
1242 debug_spew("Done processing items in model file list.\n")
1244 # Now write out netlist as before. But don't write file contents out.
1245 # **** Modified by kh to sort list of packages so Spice
1246 # **** directives, etc. (A?) are output last, and in increasing
1247 # **** order.
1249 debug_spew("Make second pass through design and "
1250 "write out a SPICE card for each component found.\n")
1251 f.write(
1252 '*============== Begin SPICE netlist of main design ============\n')
1253 if 'sort_mode' in calling_flags:
1254 # sort on refdes
1255 write_netlist(f, file_info_list, sorted(packages, cmp = packsort))
1256 else:
1257 # don't sort.
1258 write_netlist(f, file_info_list, reversed(packages))
1259 debug_spew("Done writing SPICE cards . . .\n\n")
1261 # Now write out .END(S) of netlist, depending upon whether this
1262 # schematic is a "normal schematic" or a .SUBCKT.
1264 if schematic_type != 'normal schematic':
1265 f.write('.ends %s\n' % model_name)
1266 f.write('*******************************\n')
1267 elif 'no_end_card' not in calling_flags:
1268 f.write('.end\n')
1270 debug_spew("\nOutput file is written. We are done.\n")