summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ob-spice.org228
1 files changed, 228 insertions, 0 deletions
diff --git a/ob-spice.org b/ob-spice.org
new file mode 100644
index 0000000..09ae92f
--- /dev/null
+++ b/ob-spice.org
@@ -0,0 +1,228 @@
+#+PROPERTY: header-args:emacs-lisp :tangle ob-spice-exp.el :results silent
+* License
+#+BEGIN_SRC emacs-lisp
+ ;;; ob-spice.el --- Babel Functions for spice
+ ;;; -*- coding: utf-8 -*-
+
+ ;; License: GPL v3, or any later version
+ ;;
+ ;; This file is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation; either version 3, or (at your option)
+ ;; any later version.
+ ;;
+ ;; This file is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ ;; GNU General Public License for more details.
+ ;;
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ ;;; Commentary:
+
+ ;; Org-Babel support for evaluating spice.
+
+ ;;; Requirements:
+
+ ;; - ngspice :: http://ngspice.sourceforge.net/
+
+ ;;; Code:
+
+ (require 'ob)
+#+END_SRC
+* Functionality
+
+- Temporary files in working directory are accepted (as they are
+ common in ngspice)
+- With no :file specified try to direct all outputs to a /tmp directory
+- option to return content of generated gnuplot .plt for easy use with
+ :post {generic gnuplot block}
+ - Full .plt or just data points?
+** DONE Variable replacement in spice body
+** NEXT Execution using ngspice
+*** NEXT Steps to using interactive mode
+- Output to a rawfile dumps all node data: ~ngspice -b -r x.raw
+ y.cir~. Can be loaded in interactive mode with ~load filename~.
+- Run file in interactive mode syncronously ~source input-file~ or
+ asyncronously ~aspice input-file~
+**** Comint mode to run ngspice in a buffer
+#+BEGIN_SRC emacs-lisp
+ (make-comint "spice" "ngspice")
+#+END_SRC
+** PLANNING (Auto-)Plotting
+* Code
+
+#+BEGIN_SRC emacs-lisp :tangle no
+ (add-to-list 'load-path "~/.emacs.d/lisp/ob-spice")
+#+END_SRC
+** Session handling
+#+BEGIN_SRC emacs-lisp
+ (defvar org-babel-spice-command "ngspice"
+ "Name of command to use for executing ngspice.")
+ (defun org-babel-spice-initiate-session (&optional session _params)
+ "Initiate a ngspice session.
+ Create comint buffer SESSION running ngspice."
+ (let ((sessionname (or session "spice")))
+ (make-comint sessionname org-babel-spice-command)))
+
+ (defun org-babel-prep-session:spice (session params)
+ "Prepare SESSION according to header arguments in PARAMS."
+ (let ((session (org-babel-spice-initiate-session session))
+ (var-lines (org-babel-variable-assignments:spice params)))
+ (org-babel-comint-in-buffer session
+ (sit-for .5) (goto-char (point-max))
+ (mapc (lambda (var)
+ (insert var) (comint-send-input nil t)
+ (org-babel-comint-wait-for-output session)
+ (sit-for .1) (goto-char (point-max))) var-lines))
+ session))
+ (defun org-babel-load-session:spice (session body params)
+ "Load BODY into SESSION."
+ (save-window-excursion
+ (let ((buffer (org-babel-prep-session:spice session params)))
+ (with-current-buffer buffer
+ (goto-char (process-mark (get-buffer-process (current-buffer))))
+ (insert (org-babel-chomp body)))
+ buffer)))
+
+ ;; helper
+
+ (defun org-babel-variable-assignments:spice (params)
+ "Return a list of spice statements to set the variables in PARAMS."
+ (mapcar
+ (lambda (pair)
+ (format "set %s=%s"
+ (car pair)
+ (org-babel-spice-var-to-spice (cdr pair))))
+ (org-babel--get-vars params)))
+ (defun org-babel-spice-var-to-spice (var)
+ "Convert VAR into a spice variable."
+ (if (listp var)
+ (concat "( " (mapconcat #'org-babel-spice-var-to-spice var " ") " )")
+ (format "%S" var)))
+#+END_SRC
+** NEXT Variable handling & expand body
+Don't replace variable calls in body. Instead set them in the spice
+session: ~set x=4~\\
+~set~ only sets lowercase variants of words!
+
+To set lists/arrays: ~set x=( 1 2 3 4 )~. Whitespace is important!
+Access with ~$x[0]~ or ~$x[2-len]~.
+#+BEGIN_SRC emacs-lisp
+ ;; (lambda (text) (setq body (concat text "\n" body)))
+ (defun org-babel-spice-vector-search (body vars)
+ "Replace first instance in BODY for all VARS."
+ (mapc (lambda (pair)
+ (if (string-match (format
+ "\\$%s\\[\\([0-9]\\)\\]"
+ (car pair))
+ body)
+ (let ((replacement (nth
+ (string-to-number (match-string 1 body))
+ (cadr pair))))
+ (setq body(format "%s%s%s"
+ (substring body 0 (match-beginning
+ 0))
+ replacement
+ (substring body (match-end 0)))))))
+ vars)
+ body
+ )
+#+END_SRC
+#+BEGIN_SRC emacs-lisp
+ (defun org-babel-expand-body:spice-exp (body params)
+ "Expand BODY according to PARAMS, return the expanded body."
+ (let ((vars (org-babel--get-vars params))
+ (prologue (cdr (assq :prologue params)))
+ (epilogue (cdr (assq :epilogue params)))
+ (file (cdr (assq :file params)))
+ (old-body ""))
+ ;; replace vector variables preceded by '$' and followed by the
+ ;; index in square brackets starting at 0. Matches without
+ ;; preceding or succeeding spaces.
+ (while (not (string= old-body body))
+ (setq old-body body)
+ (setq body (org-babel-spice-vector-search body vars))
+ )
+ ;; replace any variable names preceded by '$' with the actual
+ ;; value of the variable. Matches only with succeeding space or
+ ;; end of line to prevent namespace limitations.
+ (mapc (lambda (pair)
+ (setq body (replace-regexp-in-string
+ (format "\\$%s\\( \\)\\|\\$%s$" (car pair)
+ (car pair))
+ (format "%s\1" (cdr pair))
+ body)))
+ vars)
+ ;;
+
+ ;; TODO :file stuff ....
+
+ ;; add prologue/epilogue
+ (when prologue (setq body (concat prologue "\n" body)))
+ (when epilogue (setq body (concat body "\n" epilogue)))
+ body))
+#+END_SRC
+** ob-execute
+#+BEGIN_SRC emacs-lisp
+ (defun org-babel-execute:spice-exp (body params)
+ "Execute a block of Spice code with Babel.
+ This function is called by `org-babel-execute-src-block'."
+ (let ((body (org-babel-expand-body:spice-exp body params))
+ (gnuplot (cdr (assq :gnuplot params))))
+
+ ;; TODO deal with temporary files
+
+ ;;(org-babel-eval "ngspice -b " body)
+ ;; Write body to temp file & execute with ngspice comint buffer and ~source file~
+
+
+ ;; TODO read outputs from files
+
+ ;; TODO gnuplot options
+ (if (string= "yes" gnuplot)
+ nil ;return content(!) of gnuplot.plt for :post processing or
+ ;nowebbing spice into gnuplot
+ nil ;return normal spice output
+ )
+ ))
+
+
+ (provide 'ob-spice-exp)
+ ;;; ob-spice.el ends here
+#+END_SRC
+** vars
+#+BEGIN_SRC emacs-lisp
+ (defvar org-babel-spice-eoe-indicator ":org_babel_spice_eoe"
+ "String to indicate that evaluation has completed.")
+
+#+END_SRC
+* Tests
+#+BEGIN_SRC spice-exp :var x="4" :session
+
+#+END_SRC
+#+BEGIN_SRC spice-exp :var file="/tmp/spice_test" :results drawer
+ *Time Constant Measurement
+ r1 1 0 10k
+ c1 1 0 1p
+
+ .IC V(1)=1
+ .tran 1n 0.1u
+ .print tran v(1)
+ .end
+
+ .control
+ run
+ set gnuplot_terminal=png
+ *gnuplot $file v(1)
+ meas tran value_at_tau find V(1) at=1e-8
+ meas tran value_at_five_tau find V(1) at=5e-8
+ echo value_at_tau = "$&value_at_tau" > $file.txt
+ echo value_at_five_tau = "$&value_at_five_tau" >> $file.txt
+ .endc
+#+END_SRC
+
+#+RESULTS:
+:RESULTS:
+:END: