diff --git a/examples/ex_amortized_loan.py b/examples/ex_amortized_loan.py index e44d2f4..8c52e57 100644 --- a/examples/ex_amortized_loan.py +++ b/examples/ex_amortized_loan.py @@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . + + NOTE: currently this example is broken (Sep 2024) """ import sys import numpy as np @@ -25,6 +27,7 @@ from pynomo.nomographer import Nomographer from pynomo.nomo_wrapper import Nomo_Block_Type_5 +#np.seterr(all='raise') # Type 5 contour def f1(x, u): @@ -104,7 +107,7 @@ def f1(x, u): } main_params = { - 'filename': 'amortized_loan.pdf', + 'filename': 'ex_amortized_loan.pdf', 'paper_height': 20.0, 'paper_width': 20.0, 'block_params': [block_1_params, block_2_params], diff --git a/examples/ex_type5_nomo_1.py b/examples/ex_type5_nomo_1.py old mode 100644 new mode 100755 diff --git a/examples/genus1.py b/examples/genus1.py index f823794..230e0a6 100644 --- a/examples/genus1.py +++ b/examples/genus1.py @@ -83,7 +83,7 @@ def fw(u,v): 'f': lambda u: 0, # x coordinate 'g': lambda u: offset_u + scale_u*u, # y coordinate 'h': lambda u: 1, # constant - 'title': r'$u$', + 'title': r'u', 'scale_type': 'linear smart', 'tick_levels': 5, 'tick_text_levels': 3, @@ -95,7 +95,7 @@ def fw(u,v): 'f': lambda v: 1, 'g': lambda v: offset_v + scale_v*v, 'h': lambda v: 1, - 'title': r'$v$', + 'title': r'v', 'scale_type': 'linear smart', 'tick_levels': 5, 'tick_text_levels': 3, @@ -109,7 +109,7 @@ def fw(u,v): 'g': lambda w: ( offset_u*scale_v + scale_u*offset_v*w \ - scale_u*scale_v*w**3 ) / (scale_u*w+scale_v), 'h': lambda w: 1, - 'title': r'$w$', + 'title': r'w', 'title_x_shift': 10, 'scale_type': 'linear smart', 'tick_levels': 5, @@ -141,7 +141,7 @@ def fw(u,v): 'function_x': lambda u: middle_axis['f'](u/alt_per_orig), 'function_y': lambda u: middle_axis['g'](u/alt_per_orig), 'align_func': lambda u: u / alt_per_orig, - 'title': r'$9w$', + 'title': r'9w', 'title_x_shift': 7, 'title_y_shift': -3, 'scale_type': 'linear smart', @@ -159,7 +159,7 @@ def fw(u,v): main_params = { - 'filename': 'LC_filter.pdf', + 'filename': 'genus1.pdf', # a4 page, with margins approx 2cm 'paper_height': 25, # units are cm @@ -180,7 +180,6 @@ def fw(u,v): } -main_params['filename'] += '.pdf' print("printing ", main_params['filename'], " ...") Nomographer(main_params) diff --git a/nomogen/AJh.py b/nomogen/AJh.py index 9d49f33..dacaf4f 100755 --- a/nomogen/AJh.py +++ b/nomogen/AJh.py @@ -4,6 +4,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") import math @@ -11,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -103,13 +114,13 @@ def AJh(L, p): } main_params = { - 'filename': 'AJH', + 'filename': myfile, 'paper_height': 15, # units are cm 'paper_width': 10, 'title_x': 4.5, 'title_y': 1.5, 'title_box_width': 8.0, - 'title_str': r'\scriptsize $(1+L)h^2 - Lh(1+p) - {1 \over 3} (1-L)(1+2p) = 0$', + 'title_str': r'$ \scriptsize (1+L)h^2 - Lh(1+p) - {1 \over 3} (1-L)(1+2p) = 0$', 'block_params': [block_params0], 'transformations': [('scale paper',)], 'npoints': NN diff --git a/nomogen/AJp.py b/nomogen/AJp.py index 77783a4..679ba15 100755 --- a/nomogen/AJp.py +++ b/nomogen/AJp.py @@ -3,14 +3,25 @@ # nomogen example program import sys +import math + +import inspect +import os -sys.path.insert(0, "..") -from math import * +sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -38,18 +49,18 @@ def AJcheck(L, h, p): def AJp(L, h): p = ((1 + L) * h ** 2 - L * h - (1 - L) / 3) / (L * h + 2 * (1 - L) / 3) # print("result is ", (1+L)*h**2 - L*h*(1+p) - (1-L)*(1+2*p)/3) - if not isclose(AJcheck(L, h, p), 0, abs_tol=1e-10): + if not math.isclose(AJcheck(L, h, p), 0, abs_tol=1e-10): print("AJp equation failed") sys.exit("quitting") return p -Lmin = 0.5; -Lmax = 1.0; -hmin = 0.75; -hmax = 1.0; -pmin = AJp(Lmax, hmin); # <-- this clips the p scale, alternatively pmin = AJp(Lmin, hmin); -pmax = AJp(Lmin, hmax); +Lmin = 0.5 +Lmax = 1.0 +hmin = 0.75 +hmax = 1.0 +pmin = AJp(Lmax, hmin) # <-- this clips the p scale, alternatively pmin = AJp(Lmin, hmin) +pmax = AJp(Lmin, hmax) # print('pmin is ', pmin, ', pmax is ', pmax); @@ -106,7 +117,7 @@ def AJp(L, h): } main_params = { - 'filename': 'AJp', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 5, diff --git a/nomogen/GENERATE_EXAMPLES.py b/nomogen/GENERATE_EXAMPLES.py index e430321..97a54da 100644 --- a/nomogen/GENERATE_EXAMPLES.py +++ b/nomogen/GENERATE_EXAMPLES.py @@ -1,9 +1,11 @@ +#!/usr/bin/env python3 + """ - GENERATE_ALL.py + GENERATE_EXAMPLES.py Generates example nomographs. Used for testing that software package works. - Copyright (C) 20124 Leif Roschier + Copyright (C) 2024 Leif Roschier This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,9 +21,16 @@ along with this program. If not, see . """ import os -import re import time import sys +import subprocess + +showPdf = False +for a in sys.argv[1:]: + #print( "arg is ", a ) + if a == '-d': + showPdf = True + sys.path.append('../pynomo') sys.path.append('../pynomo/pynomo') @@ -32,21 +41,47 @@ if root == '.': filelist = files -filelist.remove('GENERATE_EXAMPLES.py') +if 'GENERATE_EXAMPLES.py' in filelist: filelist.remove('GENERATE_EXAMPLES.py') +if 'nomogen.py' in filelist: filelist.remove('nomogen.py') +nr_fails = 0 tic_orig = time.time() -# print filelist + for filename in filelist: - if re.compile(".py").search(filename, 1) is not None: - tic = time.time() - print("************************************") - print("executing %s" % filename) - with open(filename) as f: - code = compile(f.read(), filename, 'exec') - exec(code) - # execfile(filename) - toc = time.time() - print('Took %3.1f s for %s to execute.' % (toc - tic, filename)) - print("------------------------------------") + if filename.endswith( ".py" ): + print("\n************************************") + #print( filename ) + text = open(filename).read() + if 'Nomographer' in text: + print("executing %s" % filename) + code = compile(text, filename, 'exec') + tic = time.time() + # recover if this test fails + try: + exec(code) + except BaseException as e: + print( "test", filename, "failed -", repr(e) ) + nr_fails = nr_fails + 1 + toc = time.time() + print('Took %3.1f s for %s to execute.' % (toc - tic, filename)) + # execfile(filename) + pdffilename = filename.replace("py", "pdf" ) + + if showPdf: + import platform + if platform.system() == 'Darwin': # macOS + subprocess.call(('open', pdffilename)) + elif platform.system() == 'Windows': # Windows + os.startfile(pdffilename) + else: # linux variants + subprocess.call(('xdg-open', pdffilename)) + else: + print( 'file {} is not a nomogram file'.format(filename) ) + + toc_orig = time.time() -print('%3.1f s has elapsed overall' % (toc_orig - tic_orig)) \ No newline at end of file +print( "\nall tests passed" if nr_fails == 0 else + "1 failed test" if nr_fails == 1 else + "{} failed tests".format(nr_fails) ) +print('%3.1f s has elapsed overall' % (toc_orig - tic_orig)) + diff --git a/nomogen/air.py b/nomogen/air.py index 8abc637..f93b2ab 100755 --- a/nomogen/air.py +++ b/nomogen/air.py @@ -4,6 +4,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") import math @@ -11,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -32,15 +43,15 @@ def Q(d, v): return d * d * v * math.pi / 4 -dmin = 0.1; -dmax = 0.3; # metres -vmin = 0.3; -vmax = 4.5; # meters/sec +dmin = 0.1 +dmax = 0.3 # metres +vmin = 0.3 +vmax = 4.5 # meters/sec Qmin = Q(dmin, vmin) Qmax = Q(dmax, vmax) -print('Qmin is ', Qmin, ', Qmax is ', Qmax); +print('Qmin is ', Qmin, ', Qmax is ', Qmax) ############################################################### # # nr Chebyshev nodes needed to define the scales @@ -94,17 +105,17 @@ def Q(d, v): } main_params = { - 'filename': 'air', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 2, 'title_y': 9.0, 'title_box_width': 8.0, - 'title_str': r'\small $ Q = {\pi \over 4} d^2 v $', + 'title_str': r'$ \small Q = {\pi \over 4} d^2 v $', 'extra_texts': [ {'x': 3, 'y': 10, - 'text': r'$Air \thinspace flow \thinspace through \thinspace a \thinspace circular \thinspace duct $', + 'text': r'Air flow through a circular duct', 'width': 7, }], 'block_params': [block_params0], diff --git a/nomogen/axestest.py b/nomogen/axestest.py new file mode 100755 index 0000000..ba5bf2b --- /dev/null +++ b/nomogen/axestest.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 + +''' + nomogen test program + verify orientation of axes, for all combinations of: + v & w axis up/down + linear/log scales + symetrical/non-symetrical cases + + usage: + + axestest n + where n is an integer denoting a test number + +''' +# pylint: disable=C + +import sys +import platform +import subprocess +import os + +sys.path.insert(0, "..") + +from nomogen import Nomogen +from pynomo.nomographer import Nomographer + + +############################################################### +# +# nr Chebyshev nodes needed to define the scales +# a higher value may be necessary if the scales are very non-linear +# a lower value is faster, makes a smoother curve, +# but could be less accurate +NN = 3 + + +# simple axes tests for each test case ... + +for test_nr in range(1,17): + + if test_nr == 1: + w = lambda u,v: u+v + scale_type = 'linear smart' + title_str = r'$w = u + v$' + elif test_nr == 2: + w = lambda u, v: u - v + scale_type = 'linear smart' + title_str = r'$w = u - v$' + elif test_nr == 3: + w = lambda u, v: -(u + v) + scale_type = 'linear smart' + title_str = r'$w = -(v + u)$' + elif test_nr == 4: + w = lambda u, v: v - u + scale_type = 'linear smart' + title_str = r'$w = v - u$' + elif test_nr == 5: + w = lambda u, v: u*v + scale_type = 'log smart' + title_str = r'$w uv$' + elif test_nr == 6: + w = lambda u, v: u / v + scale_type = 'log smart' + title_str = r'$w = {u \over v}$' + elif test_nr == 7: + w = lambda u, v: 1 / (u*v) + scale_type = 'log smart' + title_str = r'$w = {1 \over {uv}}$' + elif test_nr == 8: + w = lambda u, v: v / u + scale_type = 'log smart' + title_str = r'$w = {v \over u}$' + elif test_nr == 9: + w = lambda u,v: u*u+v + scale_type = 'linear smart' + title_str = r'$w = u^2 + v$' + elif test_nr == 10: + w = lambda u, v: u*u - v + scale_type = 'linear smart' + title_str = r'$w = u^2-v$' + elif test_nr == 11: + w = lambda u, v: -(u*u + v) + scale_type = 'linear smart' + title_str = r'$w = -(u^2 + v)$' + elif test_nr == 12: + w = lambda u, v: v - u*u + scale_type = 'linear smart' + title_str = r'$w = v - u^2$' + elif test_nr == 13: + w = lambda u, v: u*u*v + scale_type = 'log smart' + title_str = r'$w = u^2v$' + elif test_nr == 14: + w = lambda u, v: u*u / v + scale_type = 'log smart' + title_str = r'$w = {u^2 \over v}$' + elif test_nr == 15: + w = lambda u, v: 1 / (u*u*v) + scale_type = 'log' # log smart -> bug in printing! + title_str = r'$w = {1 \over {u^2v}}$' + elif test_nr == 16: + w = lambda u, v: v / u / u + scale_type = 'log smart' + title_str = r'$w = {v \over {u^2}}$' + else: + sys.exit( 'there is no test number ({})'.format(test_nr) ) + + test_name = 'axtest{}'.format(test_nr) + + t = test_nr - 1 + symStr = 'symmetrical' if t %16 < 8 else 'non-symmetrical' + scaleStr = 'linear' if t % 8 < 4 else 'log' + wStr = 'up' if t % 4 < 2 else 'down' + vStr = 'up' if t % 2 == 0 else 'down' + + print( 'test "{}", {}, {}, w scale {}, v scale {}'.format(test_name, symStr, scaleStr, wStr, vStr) ) + + # range for the u scale + umin = 1 + umax = 5 + + # range for the v scale + vmin = 1 + vmax = 5 + + # automagically get the w scale range + tmp = [ w(umin, vmin), w(umax, vmin), w(umin, vmax), w(umax, vmax) ] + wmin = min(tmp) + wmax = max(tmp) + + #print( 'wmin, wmax is ', wmin, wmax) + + ############################################## + # + # definitions for the axes for pyNomo + # dictionary with key:value pairs + + left_axis = { + 'u_min': umin, + 'u_max': umax, + 'title': r'$u$', + 'scale_type': scale_type, + 'tick_levels': 3, + 'tick_text_levels': 2, + } + + right_axis = { + 'u_min': vmin, + 'u_max': vmax, + 'title': r'$v$', + 'scale_type': scale_type, + 'tick_levels': 3, + 'tick_text_levels': 2, + } + + middle_axis = { + 'u_min': wmin, + 'u_max': wmax, + 'title': r'$w$', + 'scale_type': scale_type, + 'tick_levels': 3, + 'tick_text_levels': 2, + } + + # assemble the above 3 axes into a block + block_params0 = { + 'block_type': 'type_9', + 'f1_params': left_axis, + 'f2_params': middle_axis, + 'f3_params': right_axis, + + # the isopleth connects the mid values of the outer axes + # edit this for different values + 'isopleth_values': [[(left_axis['u_min'] + left_axis['u_max']) / 2, \ + 'x', \ + (right_axis['u_min'] + right_axis['u_max']) / 2]] + } + + # the nomogram parameters + main_params = { + 'filename': test_name, + 'paper_height': 10, # units are cm + 'paper_width': 10, + 'title_x': 7.0, + 'title_y': 2.0, + 'title_box_width': 8.0, + 'title_str': title_str, + 'block_params': [block_params0], + 'transformations': [('scale paper',)], + 'npoints': NN, + + # text to appear at the foot of the nomogram + # make this null string for nothing + # a default string will appear if this is omitted + 'footer_string': '{}: {}, {}, w {}, v {}'.format(test_name, symStr, scaleStr, wStr, vStr) + } + + print("calculating the nomogram ...") + Nomogen(w, main_params) # generate nomogram for the target function + + main_params['filename'] += '.pdf' + print("printing ", main_params['filename'], " ...") + Nomographer(main_params) + + if platform.system() == 'Darwin': # macOS + subprocess.call(('open', main_params['filename'])) + elif platform.system() == 'Windows': # Windows + os.startfile(main_params['filename']) + else: # linux variants + subprocess.call(('xdg-open', main_params['filename'])) + +# TODO: check that axes are as expected + + diff --git a/nomogen/colebrookf.py b/nomogen/colebrookf.py index 34ef079..3cd67fd 100755 --- a/nomogen/colebrookf.py +++ b/nomogen/colebrookf.py @@ -4,6 +4,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from math import * @@ -11,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -70,7 +81,7 @@ def colebrookf(kond, Re): right_axis = { 'u_min': Remin, 'u_max': Remax, - 'title': r'$Reynolds \enspace nr$', + 'title': r'Reynolds nr', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -79,7 +90,7 @@ def colebrookf(kond, Re): middle_axis = { 'u_min': fmin, 'u_max': fmax, - 'title': r'$f$', + 'title': r'f', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -97,13 +108,13 @@ def colebrookf(kond, Re): } main_params = { - 'filename': 'colebrrokf', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 6.0, 'title_y': 9.0, 'title_box_width': 8.0, - 'title_str': r'$friction \thinspace in \thinspace pipes$', + 'title_str': r'friction in pipes', 'extra_texts': [ {'x': 4, 'y': 8, diff --git a/nomogen/colebrookr.py b/nomogen/colebrookr.py index 107d914..0492a99 100755 --- a/nomogen/colebrookr.py +++ b/nomogen/colebrookr.py @@ -4,6 +4,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from math import * @@ -11,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -57,7 +68,7 @@ def colebrookr(f, Re): left_axis = { 'u_min': fmin, 'u_max': fmax, - 'title': r'$f$', + 'title': r'f', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -66,7 +77,7 @@ def colebrookr(f, Re): right_axis = { 'u_min': Remin, 'u_max': Remax, - 'title': r'$Reynolds \enspace nr$', + 'title': r'Reynolds nr', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -93,13 +104,13 @@ def colebrookr(f, Re): } main_params = { - 'filename': 'colebrookr', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 6.0, 'title_y': 9.0, 'title_box_width': 8.0, - 'title_str': r'$friction \thinspace in \thinspace pipes$', + 'title_str': r'friction in pipes', 'extra_texts': [ {'x': 4, 'y': 8, diff --git a/nomogen/compound.py b/nomogen/compound.py index 06dc644..7dcdba3 100755 --- a/nomogen/compound.py +++ b/nomogen/compound.py @@ -4,11 +4,22 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -53,7 +64,7 @@ def compound(r, y): left_axis = { 'u_min': imin, 'u_max': imax, - 'title': r'$interest \thinspace rate$', + 'title': r'interest rate', 'scale_type': 'log smart', 'tick_levels': 4, 'tick_text_levels': 3, @@ -62,7 +73,7 @@ def compound(r, y): right_axis = { 'u_min': ymin, 'u_max': ymax, - 'title': r'$years$', + 'title': r'years', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -71,7 +82,7 @@ def compound(r, y): middle_axis = { 'u_min': wmin, 'u_max': wmax, - 'title': r'$final \thinspace value$', + 'title': r'final value', 'scale_type': 'log smart', 'tick_levels': 5, 'tick_text_levels': 4, @@ -87,7 +98,7 @@ def compound(r, y): } main_params = { - 'filename': 'compound', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 5.0, diff --git a/nomogen/ellipse.py b/nomogen/ellipse.py index fc8a2dd..5129b9f 100644 --- a/nomogen/ellipse.py +++ b/nomogen/ellipse.py @@ -12,6 +12,9 @@ import sys import math +import inspect +import os + sys.path.insert(0, "..") import scipy.special as sc @@ -19,6 +22,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -81,8 +92,8 @@ def circEllipse(a, b): left_axis = { 'u_min': amin, 'u_max': amax, - 'title': r'$a \enspace axis$', - 'title_x_shift': 0.5, + 'title': r'a semi axis', + 'title_x_shift': 0.7, 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -91,8 +102,8 @@ def circEllipse(a, b): right_axis = { 'u_min': bmin, 'u_max': bmax, - 'title': r'$b \thinspace axis$', - 'title_x_shift': 0.5, + 'title': r'b semi axis', + #'title_x_shift': 0.5, 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -101,7 +112,7 @@ def circEllipse(a, b): middle_axis = { 'u_min': wmin, 'u_max': wmax, - 'title': r'$circumference$', + 'title': r'circumference', 'title_x_shift': -0.2, 'scale_type': 'linear smart', 'tick_levels': 3, @@ -115,22 +126,20 @@ def circEllipse(a, b): 'f2_params': middle_axis, 'f3_params': right_axis, - # the isopleth connects the mid values of the outer axes - # edit this for different values - 'isopleth_values': [[(left_axis['u_min'] + left_axis['u_max']) / 2, \ - 'x', \ - (right_axis['u_min'] + right_axis['u_max']) / 2]] + # this isopleth connects both semi axes = 0.5 + # ie a circle of diameter 1, circumference pi + 'isopleth_values': [[0.5, 'x', 0.5]] } # the nomogram parameters main_params = { - 'filename': 'ellipse', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 7.4, 'title_y': 8.6, 'title_box_width': 8.0, - 'title_str': r'$circumference \thinspace of \thinspace an \thinspace ellipse$', + 'title_str': r'circumference of an ellipse', 'block_params': [block_params0], 'transformations': [('scale paper',)], @@ -144,7 +153,7 @@ def circEllipse(a, b): # text to appear at the foot of the nomogram # make this null string for nothing # a default string will appear if this is omitted - #'footer_string': r'$\tiny circ \enspace project \enspace configuration \enspace string$' + 'footer_string': 'ellipse project configuration string' } print("calculating the nomogram ...") diff --git a/nomogen/fv.py b/nomogen/fv.py index 421ae82..5d269ca 100755 --- a/nomogen/fv.py +++ b/nomogen/fv.py @@ -6,11 +6,22 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -63,7 +74,7 @@ def fv(r, y): left_axis = { 'u_min': rmin, 'u_max': rmax, - 'title': r'$\% \enspace rate$', + 'title': r'\% rate', 'scale_type': 'log smart', 'tick_levels': 5, 'tick_text_levels': 3, @@ -72,7 +83,7 @@ def fv(r, y): right_axis = { 'u_min': ymin, 'u_max': ymax, - 'title': r'$years$', + 'title': r'years', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -82,7 +93,7 @@ def fv(r, y): 'u_min': fvmin, 'u_max': fvmax, 'title_x_shift': 1.0, - 'title': r'$future \enspace value$', + 'title': r'future value', 'scale_type': 'log smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -101,13 +112,13 @@ def fv(r, y): main_params = { - 'filename': 'fv', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 5.0, 'title_y': 1.0, 'title_box_width': 8.0, - 'title_str': r'$future \thinspace value \thinspace of \thinspace \$1 \thinspace invested \thinspace each \thinspace year$', + 'title_str': r'future value of \$1 invested each year', 'extra_texts': [ {'x': 2, 'y': 2, diff --git a/nomogen/hiking.py b/nomogen/hiking.py index c86f5f5..4be4c93 100644 --- a/nomogen/hiking.py +++ b/nomogen/hiking.py @@ -11,14 +11,24 @@ """ # pylint: disable=C - import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -50,10 +60,9 @@ def EE(S,G): return 1.44 + t1 + t2 + t3 - # range for speed km/hr -Smin = 0.1 * 36 /10 # 0.1 m/s -> km/hr -Smax = 3 * 36 /10 # 3 m/s -> km/hr +Smin = 0.1 * 36 / 10 # 0.1 m/s -> km/hr +Smax = 3 * 36 / 10 # 3 m/s -> km/hr # range for slope Gmax = +25 @@ -84,7 +93,7 @@ def EE(S,G): 'tag': 'left', # link to alternative scale 'u_min': Smin, 'u_max': Smax, - 'title': r'$walking \thinspace speed$', + 'title': r'walking speed', 'extra_titles':[ {'dx':-2.5, 'dy':-0.0, @@ -100,7 +109,7 @@ def EE(S,G): right_axis = { 'u_min': Gmin, 'u_max': Gmax, - 'title': r'$gradient \thinspace \%$', + 'title': r'gradient \%', 'title_x_shift': 0.6, 'scale_type': 'linear smart', 'tick_levels': 5, @@ -118,7 +127,7 @@ def EE(S,G): 'extra_titles':[ {'dx':-2.0, 'dy':0.25, - 'text': r'$Expended \thinspace energy$', + 'text': r'Expended energy', }], 'scale_type': 'linear smart', 'tick_levels': 5, @@ -202,13 +211,13 @@ def EE(S,G): # the nomogram parameters main_params = { - 'filename': 'hiking', + 'filename': myfile, 'paper_height': 24, # units are cm 'paper_width': 16, 'title_x': 7.0, 'title_y': 1.0, 'title_box_width': 8.0, - 'title_str': r'$energy \thinspace expended \thinspace hiking$', + 'title_str': r'energy expended hiking', # first block is the type_9 nomogram, the dual scale type_8 blocks follow 'block_params': [block_params0, block_1_params, block_2_params], @@ -220,7 +229,7 @@ def EE(S,G): # text to appear at the foot of the nomogram # make this null string for nothing # a default string will appear if this is omitted -# 'footer_string': r'$\tiny test1 \enspace project \enspace configuration \enspace string$' +# 'footer_string': r'hiking project configuration string' } print("calculating the nomogram ...") diff --git a/nomogen/lemmon.py b/nomogen/lemmon.py index 384ec79..efd1b82 100755 --- a/nomogen/lemmon.py +++ b/nomogen/lemmon.py @@ -7,11 +7,22 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + import math # Constants associated with the density equation for normal hydrogen @@ -103,7 +114,7 @@ def Z(p, T): left_axis = { 'u_min': pmin, 'u_max': pmax, - 'title': r'$pressure \enspace MPa$', + 'title': r'Pressure MPa', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -113,7 +124,7 @@ def Z(p, T): right_axis = { 'u_min': Tmin, 'u_max': Tmax, - 'title': r'$Temperature \enspace ^\circ K$', + 'title': r'Temperature $ ^\circ $K', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -142,7 +153,7 @@ def Z(p, T): } main_params = { - 'filename': 'lemmon', + 'filename': myfile, 'paper_height': 25, # units are cm 'paper_width': 18, 'title_x': 9.0, @@ -152,12 +163,17 @@ def Z(p, T): 'extra_texts': [ {'x': 6, 'y': 4, - 'text': r'$compressibility \thinspace factor \thinspace for \thinspace hydrogen$', + 'text': r'compressibility factor for hydrogen', 'width': 10, }], 'block_params': [block_params0], 'transformations': [('scale paper',)], - 'npoints': NN + 'npoints': NN, + + # text to appear at the foot of the nomogram + # make this null string for nothing + # a default string will appear if this is omitted + 'footer_string': 'Lemmon equation' } print("calculating the nomogram ...") diff --git a/nomogen/nomogen.py b/nomogen/nomogen.py index 4391cef..ae87506 100755 --- a/nomogen/nomogen.py +++ b/nomogen/nomogen.py @@ -9,7 +9,7 @@ generate the pdf with pynomo - Copyright (C) 2021-2023 Trevor Blight + Copyright (C) 2021-2024 Trevor Blight This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +42,6 @@ import functools -from packaging.version import Version # relevant only for python >= 3.6 # pylint: disable=consider-using-f-string @@ -120,6 +119,9 @@ def __init__( self, func, main_params ): muShape = 1.0e-14 print( 'setting nomogram for best measureability, muShape is', muShape ) + global itNr + itNr = 0 + # get the min & max input values # check for sanity @@ -867,7 +869,7 @@ def report_alignment(difference, xwcoord, ywcoord): print("alignment error is estimated at less than {:5.2g} mm".format(aler)) if aler > 0.2: print("alignment errors are possible - please check.") - print("This nomogram used a polynomial of degree ", NN) + print( "This nomogram used a polynomial defined with {} points ".format(NN) ) print("Try increasing this, or reduce the range of one or more scales") # return the resulting axes into the nomogram parameters @@ -886,16 +888,28 @@ def report_alignment(difference, xwcoord, ywcoord): # get & print footer info if 'footer_string' in main_params: - txt = r'$\tiny \hfil {} \hfil$'.format( main_params['footer_string']) + txt = r'\tiny \hfil {} \hfil'.format( main_params['footer_string']) else: datestr = datetime.datetime.now().strftime("%d %b %y") if aler > 0.1: - tolstr = r",\thinspace est \thinspace tolerance \thinspace {:5.2g} mm".format(aler) + tolstr = r", est tolerance {:5.2g} mm".format(aler) else: tolstr = "" - txt = r'$\tiny \hfil {}: created \thinspace by \thinspace nomogen \thinspace {} {} \hfil$'.format( main_params['filename'].replace('\\', ' \\backslash '), datestr, tolstr) - # print("txt is \"", txt, "\"", sep='') + # '\' char is escape, '_' char is subscript, '$' is math mode, etc + escapes = "".maketrans({ '\\': r'$ \backslash $', + '^': r'\^{}', + '_': r'\_', + '~': r'$ \sim $', + '$': r'\$', + '#': r'\#', + '%': r'\%', + '&': r'\&', + '{': r'\{', + '}': r'\}' }) + txt = r'\tiny \hfil {}: created by nomogen {} {} \hfil'. \ + format( main_params['filename'].translate(escapes), datestr, tolstr) + #print("txt is \"", txt, "\"", sep='') footerText = {'x': 0, 'y': 0.0, diff --git a/nomogen/notes.odt b/nomogen/notes.odt index 3e7b618..cf60324 100644 Binary files a/nomogen/notes.odt and b/nomogen/notes.odt differ diff --git a/nomogen/pendulum.py b/nomogen/pendulum.py index 62398b4..b739e98 100755 --- a/nomogen/pendulum.py +++ b/nomogen/pendulum.py @@ -10,11 +10,22 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -62,7 +73,7 @@ def pendulum(a, b): left_axis = { 'u_min': umin, 'u_max': umax, - 'title': r'$a \thinspace distance$', + 'title': r'a distance', 'scale_type': 'linear smart', 'tick_levels': 4, 'tick_text_levels': 0, @@ -83,7 +94,7 @@ def pendulum(a, b): right_axis = { 'u_min': vmin, 'u_max': vmax, - 'title': r'$b \thinspace distance$', + 'title': r'b distance', 'scale_type': 'linear smart', 'tick_levels': 4, 'tick_text_levels': 0, @@ -104,7 +115,7 @@ def pendulum(a, b): middle_axis = { 'u_min': wmin, 'u_max': wmax, - 'title': r'$L \thinspace distance$', + 'title': r'L distance', 'scale_type': 'linear smart', 'tick_levels': 5, 'tick_text_levels': 3, @@ -123,13 +134,13 @@ def pendulum(a, b): } main_params = { - 'filename': 'pendulum', + 'filename': myfile, # a4 page, with margins approx 2cm 'paper_height': 25, # units are cm 'paper_width': 16, - 'title_x': 12.6, - 'title_y': 18.0, + 'title_x': 5.6, + 'title_y': 24.0, 'title_box_width': 3.0, 'title_str': r'$L = {{a^2 + b^2} \over {a + b}}$', 'block_params': [block_params0], diff --git a/nomogen/pension.py b/nomogen/pension.py index f5c2b36..6f66eda 100755 --- a/nomogen/pension.py +++ b/nomogen/pension.py @@ -18,11 +18,21 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + ######################################## # @@ -91,7 +101,7 @@ def amt(rq, y): 'u_min': rmin, 'u_max': rmax, 'title_x_shift': 1.0, - 'title': r'$pa \enspace pension$', + 'title': r'pa pension', 'scale_type': 'linear smart', 'tick_levels': 5, 'tick_text_levels': 3, @@ -100,7 +110,7 @@ def amt(rq, y): right_axis = { 'u_min': ymin, 'u_max': ymax, - 'title': r'$years \enspace to \enspace retirement$', + 'title': r'years to retirement', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -109,7 +119,7 @@ def amt(rq, y): middle_axis = { 'u_min': amtmin, 'u_max': amtmax, - 'title': r'$monthly \enspace contribution$', + 'title': r'monthly contribution', 'scale_type': 'log smart', 'tick_levels': 4, 'tick_text_levels': 2, @@ -128,7 +138,7 @@ def amt(rq, y): # the nomogram parameters main_params = { - 'filename': 'pension', + 'filename': myfile, 'paper_height': 25, # units are cm 'paper_width': 16, 'title_box_width': 8.0, diff --git a/nomogen/piedra.py b/nomogen/piedra.py index 50d7a6c..2a94a6b 100644 --- a/nomogen/piedra.py +++ b/nomogen/piedra.py @@ -7,6 +7,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") # se ha modificado la siguiente linea que era 'from nomogen.nomogen import Nomogen' para que se ejecute bien. @@ -14,6 +17,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -64,11 +75,11 @@ def piedra(RC, D): 'title': r'$RC$', 'title_x_shift': 0.1, 'title_y_shift': 0.5, - 'extra_titles':[ # extra title for units - {'dx':-1.3, - 'dy':0.11, + 'extra_titles': [ # extra title for units + {'dx': -1.3, + 'dy': 0.11, 'text': r'$\small MPa$', - 'width':5, + 'width': 5, }], 'scale_type': 'linear smart', 'tick_levels': 3, @@ -83,11 +94,11 @@ def piedra(RC, D): 'title': r'$D$', 'title_x_shift': 0.1, 'title_y_shift': 0.5, - 'extra_titles':[ # extra title for units - {'dx':-1.3, - 'dy':0.11, + 'extra_titles': [ # extra title for units + {'dx': -1.3, + 'dy': 0.11, 'text': r'$\small in$', - 'width':5, + 'width': 5, }], 'scale_type': 'log smart', 'tick_levels': 4, @@ -100,11 +111,11 @@ def piedra(RC, D): 'u_min': Vmin, 'u_max': Vmax, 'title': r'$V$', - 'extra_titles':[ - {'dx':-1.4, - 'dy':0.11, - 'text':r'$\small m$', - 'width':5, + 'extra_titles': [ + {'dx': -1.4, + 'dy': 0.11, + 'text': r'$\small m$', + 'width': 5, }], 'scale_type': 'linear smart', 'tick_levels': 3, @@ -124,25 +135,25 @@ def piedra(RC, D): ######## the second scales ############## # this is another type 9 nomogram with the axes overlaid on the above nomogram -#conversion factors +# conversion factors psi_per_MPa = 145.038/1000 mm_per_inch = 25.4 -feet_per_metre = 1000/25.4/12 # mm per metre / mm per inch / inches per foot +feet_per_metre = 1000/25.4/12 # mm per metre / mm per inch / inches per foot left_axis_psi = { 'tag': 'left', 'u_min': left_axis['u_min'] * psi_per_MPa, 'u_max': left_axis['u_max'] * psi_per_MPa, - 'extra_titles':[ - {'dx':-0.1, - 'dy':0.11, - 'text':r'$\small psi$', - 'width':5, + 'extra_titles': [ + {'dx': -0.1, + 'dy': 0.11, + 'text': r'$\small psi$', + 'width': 5, }], 'align_func': lambda u: u / psi_per_MPa, 'scale_type': 'linear smart', - 'text_format':r"$%3.0fk$", + 'text_format': r"%3.0fk", 'tick_levels': 5, 'tick_text_levels': 3, 'tick_side': 'right', @@ -152,11 +163,11 @@ def piedra(RC, D): 'tag': 'right', 'u_min': Dmin/mm_per_inch, 'u_max': Dmax/mm_per_inch, - 'extra_titles':[ - {'dx':-0.1, - 'dy':0.11, - 'text':r'$\small mm$', - 'width':5, + 'extra_titles': [ + {'dx': -0.1, + 'dy': 0.11, + 'text': r'$\small mm$', + 'width': 5, }], 'align_func': lambda u: u * mm_per_inch, 'scale_type': 'log smart', @@ -170,11 +181,11 @@ def piedra(RC, D): 'tag': 'middle', 'u_min': middle_axis['u_min'] * feet_per_metre, 'u_max': middle_axis['u_max'] * feet_per_metre, - 'extra_titles':[ - {'dx':-0.1, - 'dy':0.11, - 'text':r'$\small ft$', - 'width':5, + 'extra_titles': [ + {'dx': -0.1, + 'dy': 0.11, + 'text': r'$\small ft$', + 'width': 5, }], 'align_func': lambda u: u / feet_per_metre, 'scale_type': 'linear smart', @@ -184,23 +195,23 @@ def piedra(RC, D): } -block_1_params={ - 'block_type':'type_9', +block_1_params = { + 'block_type': 'type_9', 'f1_params': left_axis_psi, 'f2_params': middle_axis_feet, 'f3_params': right_axis_in, - 'isopleth_values': [[ 'x','x','x' ]] + 'isopleth_values': [['x', 'x', 'x']] } main_params = { - 'filename': 'piedra', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 4.0, 'title_y': 9.0, 'title_box_width': 8.0, - 'title_str': r'$piedra \thinspace en \thinspace voladuras$', + 'title_str': r'piedra en voladuras', 'extra_texts': [ {'x': 2, 'y': 8, @@ -222,20 +233,17 @@ def piedra(RC, D): print("printing ", main_params['filename'], " ...") Nomographer(main_params) -sys.exit() # check function for both sides of dual scales: +#print( 'checking dual scales' ) for i in [left_axis, left_axis_psi, right_axis, right_axis_in]: + break # omit checks for k in range(11): t = i['u_min']*(10-k)/10 + i['u_max']*k/10 if 'f' in i: print( i['tick_side'], ",", ":", t, i['f'](t), i['g'](t) ) elif 'function_x' in i: - print( i['tick_side'], ",", ":", t, \ + print( i['tick_side'], ",", ":", t, i['function_x'](t), i['function_y'](t) ) else: print( "'f' not in ", i['tick_side'] ) - - - - diff --git a/nomogen/recip.py b/nomogen/recip.py index 6fa6b5b..1d39f72 100755 --- a/nomogen/recip.py +++ b/nomogen/recip.py @@ -6,11 +6,22 @@ import sys +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -92,7 +103,7 @@ def recip(u, v): # the nomogram parameters main_params = { - 'filename': 'recip', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 3.0, diff --git a/nomogen/sq.py b/nomogen/sq.py index 4cf84c1..0468055 100755 --- a/nomogen/sq.py +++ b/nomogen/sq.py @@ -5,11 +5,22 @@ import sys import math +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -51,7 +62,7 @@ def sq(u, v): left_axis = { 'u_min': umin, 'u_max': umax, - 'title': r'$u \enspace value$', + 'title': r'u value', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -60,7 +71,7 @@ def sq(u, v): right_axis = { 'u_min': vmin, 'u_max': vmax, - 'title': r'$v \enspace scale$', + 'title': r'v scale', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -69,7 +80,7 @@ def sq(u, v): middle_axis = { 'u_min': wmin, 'u_max': wmax, - 'title': r'$w \thinspace scale$', + 'title': r'w scale', 'scale_type': 'log smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -87,7 +98,7 @@ def sq(u, v): } main_params = { - 'filename': 'sq', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 7.0, diff --git a/nomogen/test1.py b/nomogen/test1.py index 4e57049..57571d3 100755 --- a/nomogen/test1.py +++ b/nomogen/test1.py @@ -7,11 +7,21 @@ import sys import math +import inspect +import os + sys.path.insert(0, "..") from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + ######################################## # @@ -63,7 +73,7 @@ def test1(u, v): left_axis = { 'u_min': umin, 'u_max': umax, - 'title': r'$u \enspace scale$', + 'title': r'u scale', 'title_x_shift': 0.5, 'scale_type': 'linear smart', 'tick_levels': 3, @@ -73,7 +83,7 @@ def test1(u, v): right_axis = { 'u_min': vmin, 'u_max': vmax, - 'title': r'$v \thinspace scale$', + 'title': r'v scale', 'title_x_shift': 0.5, 'scale_type': 'linear smart', 'tick_levels': 3, @@ -83,7 +93,7 @@ def test1(u, v): middle_axis = { 'u_min': wmin, 'u_max': wmax, - 'title': r'$w \thinspace scale$', + 'title': r'w scale', 'title_x_shift': -0.2, 'scale_type': 'linear smart', 'tick_levels': 3, @@ -105,8 +115,9 @@ def test1(u, v): } # the nomogram parameters + main_params = { - 'filename': 'test1', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 7.0, @@ -124,10 +135,10 @@ def test1(u, v): #'muShape': 0, # text to appear at the foot of the nomogram - # note tha latex rules apply + # note that latex rules apply # a default string will appear if this is omitted # make this an empty string to have no footer text - 'footer_string': r'$\tiny test1 \enspace project \enspace footer \enspace string$' + 'footer_string': 'test1 project footer string' } print("calculating the nomogram ...") diff --git a/nomogen/user_guide.odt b/nomogen/user_guide.odt index 098c995..2534ea8 100644 Binary files a/nomogen/user_guide.odt and b/nomogen/user_guide.odt differ diff --git a/nomogen/wire.py b/nomogen/wire.py old mode 100755 new mode 100644 index 474ee7f..7008559 --- a/nomogen/wire.py +++ b/nomogen/wire.py @@ -4,6 +4,9 @@ import sys +import inspect +import os + sys.path.insert(0, "..") import math @@ -11,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # @@ -58,7 +69,7 @@ def W(m, d): left_axis = { 'u_min': mmin, 'u_max': mmax, - 'title': r'$m \thinspace kN$', + 'title': r'$m$ kN', 'scale_type': 'log smart', 'tick_levels': 5, 'tick_text_levels': 4, @@ -68,7 +79,7 @@ def W(m, d): 'u_min': dmin, 'u_max': dmax, # 'title_x_shift': 0.5, - 'title': r'$d \thinspace mm$', + 'title': r'$d$ mm', 'scale_type': 'log smart', 'tick_levels': 5, 'tick_text_levels': 4, @@ -78,7 +89,7 @@ def W(m, d): 'u_min': Wmin, 'u_max': Wmax, # 'title_x_shift': -0.5, - 'title': r'$W \thinspace MPa$', + 'title': r'$W$ MPa', 'scale_type': 'log smart', 'tick_levels': 4, 'tick_text_levels': 2, @@ -96,17 +107,17 @@ def W(m, d): } main_params = { - 'filename': 'wire', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 7, 'title_y': 8.1, 'title_box_width': 8.0, - 'title_str': r'\small $ W = {m \over {{\pi \over 4} d^2 }}$', + 'title_str': r'\Large $ W = {m \over {{\pi \over 4} d^2 }}$', 'extra_texts': [ {'x': 5.2, 'y': 8.8, - 'text': r'\small $breaking \thinspace strain \thinspace of \thinspace a \thinspace wire $', + 'text': r'breaking strain of a wire', 'width': 7, }], 'block_params': [block_params0], diff --git a/nomogen/yrs.py b/nomogen/yrs.py index 5e08249..6b2adc4 100755 --- a/nomogen/yrs.py +++ b/nomogen/yrs.py @@ -3,6 +3,10 @@ #nomogen example program import sys + +import inspect +import os + sys.path.insert(0, "..") from math import * @@ -10,6 +14,14 @@ from nomogen import Nomogen from pynomo.nomographer import Nomographer +# get current file name +myfile = os.path.basename(inspect.stack()[0][1]).replace(".py", "") + +# alternative with no external dependencies - it works most of the time +# myfile = __name__ == "__main__" and (__file__.endswith(".py") and __file__.replace(".py", "") or "nomogen") +# or __name__, + + ######################################## # # this is the target function, @@ -56,7 +68,7 @@ def yrs(fv, r): left_axis = { 'u_min': fvmin, 'u_max': fvmax, - 'title': r'$future \enspace value$', + 'title': r'future value', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -65,7 +77,7 @@ def yrs(fv, r): right_axis = { 'u_min': imin, 'u_max': imax, - 'title': r'$interest \enspace rate$', + 'title': r'interest rate', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -74,7 +86,7 @@ def yrs(fv, r): middle_axis = { 'u_min': ymin, 'u_max': ymax, - 'title': r'$years$', + 'title': r'years', 'scale_type': 'linear smart', 'tick_levels': 3, 'tick_text_levels': 2, @@ -93,13 +105,13 @@ def yrs(fv, r): } main_params = { - 'filename': 'yrs', + 'filename': myfile, 'paper_height': 10, # units are cm 'paper_width': 10, 'title_x': 6.0, 'title_y': 1.0, 'title_box_width': 8.0, - 'title_str':r'$future \thinspace value \thinspace of \thinspace \$1 \thinspace invested \thinspace each \thinspace year$', + 'title_str':r'future value of \$1 invested each year', 'extra_texts':[ {'x':5, 'y':2, diff --git a/pynomo/isopleth.py b/pynomo/isopleth.py index c2f0eac..b6b26e4 100644 --- a/pynomo/isopleth.py +++ b/pynomo/isopleth.py @@ -20,7 +20,8 @@ import math import pyx import copy, re -from scipy.optimize import * +#from scipy.optimize import * +import scipy.optimize from numpy import arange import warnings @@ -671,8 +672,8 @@ def parse_color(self, color_str): return pyx.color.cmyk.Periwinkle if re.match("CadetBlue", color_str, re.IGNORECASE): return pyx.color.cmyk.CadetBlue - if re.match("CornFlowerBlue", color_str, re.IGNORECASE): - return pyx.color.cmyk.CornFlowerBlue + if re.match("CornflowerBlue", color_str, re.IGNORECASE): + return pyx.color.cmyk.CornflowerBlue if re.match("MidnightBlue", color_str, re.IGNORECASE): return pyx.color.cmyk.MidnightBlue if re.match("NavyBlue", color_str, re.IGNORECASE): @@ -693,8 +694,8 @@ def parse_color(self, color_str): return pyx.color.cmyk.Turquoise if re.match("TealBlue", color_str, re.IGNORECASE): return pyx.color.cmyk.TealBlue - if re.match("AquaMarine", color_str, re.IGNORECASE): - return pyx.color.cmyk.AquaMarine + if re.match("Aquamarine", color_str, re.IGNORECASE): + return pyx.color.cmyk.Aquamarine if re.match("BlueGreen", color_str, re.IGNORECASE): return pyx.color.cmyk.BlueGreen if re.match("Emerald", color_str, re.IGNORECASE): @@ -732,7 +733,7 @@ def parse_color(self, color_str): if re.match("White", color_str, re.IGNORECASE): return pyx.color.cmyk.White # default - print("unknown color: %s" % color) + print("unknown color: %s" % color_str) return pyx.color.cmyk.Black def parse_isopleth_params(self, params): @@ -1126,7 +1127,7 @@ def xy_v_u(self, v, u): # print "x_range:" # print x_range # use complex numbers to filter results with complex part - #values = func_opt(x_range.astype(complex)) + # values = func_opt(x_range.astype(complex)) values = x_range values_list_complex = values.tolist() values_list = [] @@ -1134,16 +1135,16 @@ def xy_v_u(self, v, u): if value.imag == 0: values_list.append(value.real) else: - values_list.append(1e12) # large number + values_list.append(math.inf) # large number # print "values_list:" # print values_list min_x_idx = values_list.index(min(values_list)) - x_init = x_range[min_x_idx] + x_init = x_range[min_x_idx].real # print "x_start %g"%x_start # print "x_stop %g"%x_stop # print "x_init %g"%x_init # find x point where u meets v = optimization - x_opt = fmin(func_opt, [x_init], disp=0, maxiter=1e5, maxfun=1e5, ftol=1e-8, xtol=1e-8)[0] + x_opt = scipy.optimize.fmin(func_opt, [x_init], disp=0, maxiter=1e5, maxfun=1e5, ftol=1e-8, xtol=1e-8)[0] x_transformed = self.nomo_block._give_trafo_x_(x_opt, u_value) y_transformed = self.nomo_block._give_trafo_y_(x_opt, u_value) return x_transformed, y_transformed, x_opt, u_value @@ -1193,7 +1194,7 @@ def wd_x_y_interp(self, x, y): distance_1 = self._calc_distance_points_(x1s, y1s, x, y) distance_2 = self._calc_distance_points_(x2s, y2s, x, y) distance = min(distance_1, distance_2) - if min_distance == None: + if min_distance is None: min_distance = distance closest_value = self.nomo_block.atom_wd.section_values[idx][0] else: @@ -1218,7 +1219,7 @@ def u_x_y_interp(self, x, y): distance_1 = self._calc_distance_points_(x1s, y1s, x, y) distance_2 = self._calc_distance_points_(x2s, y2s, x, y) distance = min(distance_1, distance_2) - if min_distance == None: + if min_distance is None: min_distance = distance closest_value = self.nomo_block.atom_u.section_values[idx][0] else: diff --git a/pynomo/nomo_axis.py b/pynomo/nomo_axis.py index 5eaa0ab..7b1d8ae 100644 --- a/pynomo/nomo_axis.py +++ b/pynomo/nomo_axis.py @@ -20,6 +20,7 @@ import pyx import math import scipy +import numpy import random import copy #, re, pprint import six # for python 2 and 3 compatibility @@ -1119,8 +1120,8 @@ def _make_log_axis_old(self, start, stop, f, g, turn=1): thin_line = pyx.path.path(pyx.path.moveto(f(min), g(min))) max_decade = math.ceil(math.log10(max)) min_decade = math.floor(math.log10(min)) - for decade in scipy.arange(min_decade, max_decade + 1, 1): - for number in scipy.concatenate((scipy.arange(1, 2, 0.2), scipy.arange(2, 3, 0.5), scipy.arange(3, 10, 1))): + for decade in numpy.arange(min_decade, max_decade + 1, 1): + for number in scipy.concatenate((numpy.arange(1, 2, 0.2), numpy.arange(2, 3, 0.5), numpy.arange(3, 10, 1))): u = number * 10.0 ** decade if (u - min) > 0: # to avoid too big du values du = (u - min) * 1e-6 @@ -1814,8 +1815,8 @@ def find_log_ticks(start, stop): min_decade = math.floor(math.log10(min)) start_ax = None stop_ax = None - for decade in scipy.arange(min_decade, max_decade + 1, 1): - # for number in scipy.concatenate((scipy.arange(1,2,0.2),scipy.arange(2,3,0.5),scipy.arange(3,10,1))): + for decade in numpy.arange(min_decade, max_decade + 1, 1): + # for number in scipy.concatenate((numpy.arange(1,2,0.2),numpy.arange(2,3,0.5),numpy.arange(3,10,1))): for number in [1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3, 4, 5, 6, 7, 8, 9]: u = number * 10.0 ** decade if u >= min and u <= max: @@ -1865,7 +1866,7 @@ def find_log_ticks_smart(start, stop, f, g, turn=1, base_start=None, tick_2_list_final = tick_2_list_final + tick_1_list tick_3_list_final = tick_3_list_final + tick_2_list tick_4_list_final = tick_4_list_final + tick_3_list + tick_4_list - for decade in scipy.arange(min_decade + 1, max_decade, 1): + for decade in numpy.arange(min_decade + 1, max_decade, 1): value = 10.0 ** decade start = value stop = min(value * 10.0, max_value) @@ -1953,9 +1954,9 @@ def find_tick_directions(list, f, g, side, start, stop, full_angle=False, extra_ angle = -math.atan(dx_unit / dy_unit) * 180.0 / math.pi else: angle = 0.0 - if scipy.sign(dx_unit) < 0.0 and scipy.sign(dy_unit) < 0.0: + if numpy.sign(dx_unit) < 0.0 and numpy.sign(dy_unit) < 0.0: angle = angle - 180.0 - if scipy.sign(dy_unit) < 0.0 <= scipy.sign(dx_unit): + if numpy.sign(dy_unit) < 0.0 <= numpy.sign(dx_unit): angle += 180.0 angle += extra_angle dx_units.append(dx_unit) diff --git a/pynomo/nomo_grid_box.py b/pynomo/nomo_grid_box.py index 767bce5..f54b440 100644 --- a/pynomo/nomo_grid_box.py +++ b/pynomo/nomo_grid_box.py @@ -442,8 +442,9 @@ def func_top(x): value = func2(x.astype(complex), v) if value.imag > 0: return 1e10 # big number - else: - return (func2(x, v) - max_fu) ** 2 + if value.imag < 0: + return -1e10 # big number + return (func2(x, v) - max_fu) ** 2 def func_bottom(x): value = func2(x.astype(complex), v) diff --git a/pynomo/nomo_wrapper.py b/pynomo/nomo_wrapper.py index b6bd8d2..c35a0f0 100644 --- a/pynomo/nomo_wrapper.py +++ b/pynomo/nomo_wrapper.py @@ -83,35 +83,35 @@ def _return_initial_shift_(self): gamma3 = 1.0 return alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 - # def _calc_trafo_(self, x1, y1, x2, y2, x3, y3, x1d, y1d, x2d, y2d, x3d, y3d): - # """ - # transforms three points to three points via rotation and scaling - # and transformation - # xd = alpha1*x+beta1*y+gamma1 - # yd = alpha2*x+beta2*y+gamma2 - # alpha3=0, beta3=0, gamma3=1.0 - # """ - # matt = np.array([[x1, y1, 1.0, 0.0, 0.0, 0.0], - # [0.0, 0.0, 0.0, x1, y1, 1.0], - # [x2, y2, 1.0, 0.0, 0.0, 0.0], - # [0.0, 0.0, 0.0, x2, y2, 1.0], - # [x3, y3, 1.0, 0.0, 0.0, 0.0], - # [0.0, 0.0, 0.0, x3, y3, 1.0]]) - # # print rank(mat) - # inverse = np.linalg.inv(matt) - # # vec=dot(inverse,[[x1d,y1d,x2d,y2d,x3d,y3d]]) - # dest = np.array([x1d, y1d, x2d, y2d, x3d, y3d]) - # vec = np.linalg.solve(matt, dest) - # alpha1 = vec[0] - # beta1 = vec[1] - # gamma1 = vec[2] - # alpha2 = vec[3] - # beta2 = vec[4] - # gamma2 = vec[5] - # alpha3 = 0.0 - # beta3 = 0.0 - # gamma3 = 1.0 - # return alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 + def _calc_trafo_(self, x1, y1, x2, y2, x3, y3, x1d, y1d, x2d, y2d, x3d, y3d): + """ + transforms three points to three points via rotation and scaling + and transformation + xd = alpha1*x+beta1*y+gamma1 + yd = alpha2*x+beta2*y+gamma2 + alpha3=0, beta3=0, gamma3=1.0 + """ + matt = np.array([[x1, y1, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, x1, y1, 1.0], + [x2, y2, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, x2, y2, 1.0], + [x3, y3, 1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, x3, y3, 1.0]]) + # print rank(mat) + inverse = np.linalg.inv(matt) + # vec=dot(inverse,[[x1d,y1d,x2d,y2d,x3d,y3d]]) + dest = np.array([x1d, y1d, x2d, y2d, x3d, y3d]) + vec = np.linalg.solve(matt, dest) + alpha1 = vec[0] + beta1 = vec[1] + gamma1 = vec[2] + alpha2 = vec[3] + beta2 = vec[4] + gamma2 = vec[5] + alpha3 = 0.0 + beta3 = 0.0 + gamma3 = 1.0 + return alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 def _update_trafo_(self): """ @@ -304,12 +304,97 @@ def _draw_extra_texts_(self, c): c.text(x, y, text_str, [ pyx.text.parbox(width)] + pyx_extra_defs) - def align_blocks(self): + def align_blocks_old(self): """ aligns blocks w.r.t. each other according to 'tag' fields in Atom params dictionary """ + # # translate all blocks initially + # for block in self.block_stack: + # alpha1,beta1,gamma1,alpha2,beta2,gamma2,alpha3,beta3,gamma3=\ + # self._return_initial_shift_() + # block.add_transformation(alpha1,beta1,gamma1, + # alpha2,beta2,gamma2, + # alpha3,beta3,gamma3) + for idx1, block1 in enumerate(self.block_stack): + for idx2, block2 in enumerate(self.block_stack): + if idx2 > idx1: + for atom1 in block1.atom_stack: + for atom2 in block2.atom_stack: + if atom1.params['tag'] == atom2.params['tag'] \ + and not atom1.params['tag'] == 'none' \ + and not atom2.params['aligned']: # align only once + # let's see if need for double align + double_aligned = False + for atom1d in block1.atom_stack: + for atom2d in block2.atom_stack: + if atom1d.params['dtag'] == atom2d.params['dtag'] \ + and not atom1d.params['dtag'] == 'none': + # and not atom1d.params['tag']==atom1.params['tag']: + # and not atom2d.params['aligned']: # align only once + # do first pre-alignment + # alpha1,beta1,gamma1,alpha2,beta2,gamma2,alpha3,beta3,gamma3=\ + # self._find_trafo_2_atoms_(atom1,atom2) + # block2.add_transformation(alpha1,beta1,gamma1, + # alpha2,beta2,gamma2, + # alpha3,beta3,gamma3) + # double alignment + # print "double aligning with tags %s %s" % ( + # atom1.params['tag'], atom1d.params['dtag']) + # alpha1,beta1,gamma1,alpha2,beta2,gamma2,alpha3,beta3,gamma3=\ + # self._find_trafo_4_atoms_3_points_(atom1,atom1d,atom2,atom2d) + # block2.add_transformation(alpha1,beta1,gamma1, + # alpha2,beta2,gamma2, + # alpha3,beta3,gamma3) + alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 = \ + self._find_trafo_4_atoms_( + atom1, atom1d, atom2, atom2d) + block2.add_transformation(alpha1, beta1, gamma1, + alpha2, beta2, gamma2, + alpha3, beta3, gamma3) + double_aligned = True + # # DEBUG + # u_start_1=min(atom1.params['u_min'],atom1.params['u_max']) + # u_stop_1=max(atom1.params['u_min'],atom1.params['u_max']) + # u_start_1d=min(atom1d.params['u_min'],atom2.params['u_max']) + # u_stop_1d=max(atom1d.params['u_min'],atom2.params['u_max']) + # print "test if same:" + # print atom1.give_x(u_start_1) + # print atom1d.give_x(u_start_1d) + # print atom1.give_y(u_start_1) + # print atom1d.give_y(u_start_1d) + # print atom2.give_x(u_start_1) + # print atom2d.give_x(u_start_1d) + # print atom2.give_y(u_start_1) + # print atom2d.give_y(u_start_1d) + # print idx2 + # print idx2 + if not double_aligned: + # print "Aligning with tag %s" % atom1.params['tag'] + alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 = \ + self._find_trafo_2_atoms_(atom1, atom2) + block2.add_transformation(alpha1, beta1, gamma1, + alpha2, beta2, gamma2, + alpha3, beta3, gamma3) + # align only once + atom2.params['aligned'] = True + # let's make identity matrix that will be changed when optimized + for block in self.block_stack: + block.add_transformation() + + def align_blocks(self): + """ + aligns blocks w.r.t. each other according to 'tag' fields + in Atom params dictionary + """ + # # translate all blocks initially + # for block in self.block_stack: + # alpha1,beta1,gamma1,alpha2,beta2,gamma2,alpha3,beta3,gamma3=\ + # self._return_initial_shift_() + # block.add_transformation(alpha1,beta1,gamma1, + # alpha2,beta2,gamma2, + # alpha3,beta3,gamma3) for idx1, block1 in enumerate(self.block_stack): for idx2, block2 in enumerate(self.block_stack): @@ -346,6 +431,7 @@ def align_blocks(self): block2.add_transformation(alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3) + # atom2.params['aligned']=True # align only once block2.aligned = True # align only once # let's make identity matrix that will be changed when optimized for block in self.block_stack: @@ -467,7 +553,7 @@ def find_coords(atom1, atom2): (x3d, y3d), (x4d, y4d), (x5d, y5d)] - ) + ) # print (alpha1,beta1,gamma1,alpha2,beta2,gamma2,alpha3,beta3,gamma3) return alpha1, beta1, gamma1, alpha2, beta2, gamma2, alpha3, beta3, gamma3 @@ -476,6 +562,7 @@ def _calc_transformation_matrix_overdetermined_(self, coord_pairs, dest_coord_pa """ svd-based solving of affine transformation between two sets of coordinates """ + def _make_row_(coordinate='x', x=1.0, y=1.0, coord_value=1.0): """ Utility to find transformation matrix. See eq.37,a in Allcock. @@ -1396,12 +1483,12 @@ def set_block(self, height=10.0, width=10.0, float_axis='F1 or F2', padding=0.9, class Nomo_Block_Type_5(Nomo_Block): """ v - -------------------- - | \ \ | y - u |----\----\--------| w | Diagonal "line_func" missing in pic. - |-----\----\-------| | Pic. without mirrorings. - | \ \ | |-----> x - -------------------- + ---------------------- + | \\ \\ | y + u |----\\----\\--------| w | Diagonal "line_func" missing in pic. + |-----\\----\\-------| | Pic. without mirrorings. + | \\ \\ | |-----> x + --------------------- wd u,v relate to coordinates x,y in rectangle as func_u(u)=y @@ -1552,6 +1639,8 @@ def _draw_v_text_(self, x, y, dx, dy, canvas, title, title_title='', x_corr=0.0, """" draws titles to v-contours """ + title_text = "" + text_attr = [] # to be filled para_v = self.grid_box.params_v if np.sqrt(dx ** 2 + dy ** 2) == 0: dx_unit = 0 @@ -1640,7 +1729,7 @@ def _draw_horizontal_guides_(self, canvas, axis_color=pyx.color.cmyk.Gray): if p['horizontal_guides']: line = pyx.path.path() nr = p['horizontal_guide_nr'] - for y in scipy.linspace(self.grid_box.y_top, self.grid_box.y_bottom, nr): + for y in np.linspace(self.grid_box.y_top, self.grid_box.y_bottom, nr): xt1 = self._give_trafo_x_(self.grid_box.x_left, y) yt1 = self._give_trafo_y_(self.grid_box.x_left, y) xt2 = self._give_trafo_x_(self.grid_box.x_right, y) @@ -1659,7 +1748,7 @@ def _draw_vertical_guides_(self, canvas, axis_color=pyx.color.cmyk.Gray): if p['vertical_guides']: line = pyx.path.path() nr = p['vertical_guide_nr'] - for x in scipy.linspace(self.grid_box.x_left, self.grid_box.x_right, nr): + for x in np.linspace(self.grid_box.x_left, self.grid_box.x_right, nr): xt1 = self._give_trafo_x_(x, self.grid_box.y_top) yt1 = self._give_trafo_y_(x, self.grid_box.y_top) xt2 = self._give_trafo_x_(x, self.grid_box.y_bottom)