diff options
-rw-r--r-- | ob-spice.org | 228 |
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: |