source: sasmodels/doc/genmodel.py @ a6d3f46

Last change on this file since a6d3f46 was b866abf, checked in by pkienzle, 7 years ago

make sure doc build doesn't require access to display

  • Property mode set to 100644
File size: 6.5 KB
Line 
1from __future__ import print_function
2
3import sys, os, math, re
4import numpy as np
5import matplotlib
6matplotlib.use('Agg')
7import matplotlib.pyplot as plt
8sys.path.insert(0, os.path.abspath('..'))
9from sasmodels import generate, core
10from sasmodels.direct_model import DirectModel, call_profile
11from sasmodels.data import empty_data1D, empty_data2D
12
13try:
14    from typing import Dict, Any
15except ImportError:
16    pass
17else:
18    from matplotlib.axes import Axes
19    from sasmodels.kernel import KernelModel
20    from sasmodels.modelinfo import ModelInfo
21
22
23def plot_1d(model, opts, ax):
24    # type: (KernelModel, Dict[str, Any], Axes) -> None
25    """
26    Create a 1-D image.
27    """
28    q_min, q_max, nq = opts['q_min'], opts['q_max'], opts['nq']
29    q_min = math.log10(q_min)
30    q_max = math.log10(q_max)
31    q = np.logspace(q_min, q_max, nq)
32    data = empty_data1D(q)
33    calculator = DirectModel(data, model)
34    Iq1D = calculator()
35
36    ax.plot(q, Iq1D, color='blue', lw=2, label=model.info.name)
37    ax.set_xlabel(r'$Q \/(\AA^{-1})$')
38    ax.set_ylabel(r'$I(Q) \/(\mathrm{cm}^{-1})$')
39    ax.set_xscale(opts['xscale'])
40    ax.set_yscale(opts['yscale'])
41    #ax.legend(loc='best')
42
43def plot_2d(model, opts, ax):
44    # type: (KernelModel, Dict[str, Any], Axes) -> None
45    """
46    Create a 2-D image.
47    """
48    qx_max, nq2d = opts['qx_max'], opts['nq2d']
49    q = np.linspace(-qx_max, qx_max, nq2d) # type: np.ndarray
50    data2d = empty_data2D(q, resolution=0.0)
51    calculator = DirectModel(data2d, model)
52    Iq2D = calculator() #background=0)
53    Iq2D = Iq2D.reshape(nq2d, nq2d)
54    if opts['zscale'] == 'log':
55        Iq2D = np.log(np.clip(Iq2D, opts['vmin'], np.inf))
56    ax.imshow(Iq2D, interpolation='nearest', aspect=1, origin='lower',
57              extent=[-qx_max, qx_max, -qx_max, qx_max], cmap=opts['colormap'])
58    ax.set_xlabel(r'$Q_x \/(\AA^{-1})$')
59    ax.set_ylabel(r'$Q_y \/(\AA^{-1})$')
60
61def plot_profile_inset(model_info, ax):
62    p = ax.get_position()
63    width, height = 0.4*(p.x1-p.x0), 0.4*(p.y1-p.y0)
64    left, bottom = p.x1-width, p.y1-height
65    inset = plt.gcf().add_axes([left, bottom, width, height])
66    x, y, labels = call_profile(model_info)
67    inset.plot(x, y, '-')
68    inset.locator_params(nbins=4)
69    #inset.set_xlabel(labels[0])
70    #inset.set_ylabel(labels[1])
71    inset.text(0.99, 0.99, "profile",
72               horizontalalignment="right",
73               verticalalignment="top",
74               transform=inset.transAxes)
75
76def figfile(model_info):
77    # type: (ModelInfo) -> str
78    return model_info.id + '_autogenfig.png'
79
80def make_figure(model_info, opts):
81    # type: (ModelInfo, Dict[str, Any]) -> None
82    """
83    Generate the figure file to include in the docs.
84    """
85    model = core.build_model(model_info)
86
87    fig_height = 3.0 # in
88    fig_left = 0.6 # in
89    fig_right = 0.5 # in
90    fig_top = 0.6*0.25 # in
91    fig_bottom = 0.6*0.75
92    if model_info.parameters.has_2d:
93        plot_height = fig_height - (fig_top+fig_bottom)
94        plot_width = plot_height
95        fig_width = 2*(plot_width + fig_left + fig_right)
96        aspect = (fig_width, fig_height)
97        ratio = aspect[0]/aspect[1]
98        ax_left = fig_left/fig_width
99        ax_bottom = fig_bottom/fig_height
100        ax_height = plot_height/fig_height
101        ax_width = ax_height/ratio # square axes
102        fig = plt.figure(figsize=aspect)
103        ax2d = fig.add_axes([0.5+ax_left, ax_bottom, ax_width, ax_height])
104        plot_2d(model, opts, ax2d)
105        ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height])
106        plot_1d(model, opts, ax1d)
107        #ax.set_aspect('square')
108    else:
109        plot_height = fig_height - (fig_top+fig_bottom)
110        plot_width = (1+np.sqrt(5))/2*fig_height
111        fig_width = plot_width + fig_left + fig_right
112        ax_left = fig_left/fig_width
113        ax_bottom = fig_bottom/fig_height
114        ax_width = plot_width/fig_width
115        ax_height = plot_height/fig_height
116        aspect = (fig_width, fig_height)
117        fig = plt.figure(figsize=aspect)
118        ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height])
119        plot_1d(model, opts, ax1d)
120
121    if model_info.profile:
122        plot_profile_inset(model_info, ax1d)
123
124    # Save image in model/img
125    path = os.path.join('model', 'img', figfile(model_info))
126    plt.savefig(path, bbox_inches='tight')
127    #print("figure saved in",path)
128
129def gen_docs(model_info):
130    # type: (ModelInfo) -> None
131    """
132    Generate the doc string with the figure inserted before the references.
133    """
134
135    # Load the doc string from the module definition file and store it in rst
136    docstr = generate.make_doc(model_info)
137
138    # Auto caption for figure
139    captionstr = '\n'
140    captionstr += '.. figure:: img/' + figfile(model_info) + '\n'
141    captionstr += '\n'
142    if model_info.parameters.has_2d:
143        captionstr += '    1D and 2D plots corresponding to the default parameters of the model.\n'
144    else:
145        captionstr += '    1D plot corresponding to the default parameters of the model.\n'
146    captionstr += '\n'
147
148    # Add figure reference and caption to documentation (at end, before References)
149    pattern = '\*\*REFERENCE'
150    match = re.search(pattern, docstr.upper())
151
152    if match:
153        docstr1 = docstr[:match.start()]
154        docstr2 = docstr[match.start():]
155        docstr = docstr1 + captionstr + docstr2
156    else:
157        print('------------------------------------------------------------------')
158        print('References NOT FOUND for model: ', model_info.id)
159        print('------------------------------------------------------------------')
160        docstr += captionstr
161
162    open(sys.argv[2],'w').write(docstr)
163
164def process_model(path):
165    # type: (str) -> None
166    """
167    Generate doc file and image file for the given model definition file.
168    """
169
170    # Load the model file
171    model_name = os.path.basename(path)[:-3]
172    model_info = core.load_model_info(model_name)
173
174    # Plotting ranges and options
175    opts = {
176        'xscale'    : 'log',
177        'yscale'    : 'log' if not model_info.structure_factor else 'linear',
178        'zscale'    : 'log' if not model_info.structure_factor else 'linear',
179        'q_min'     : 0.001,
180        'q_max'     : 1.0,
181        'nq'        : 1000,
182        'nq2d'      : 1000,
183        'vmin'      : 1e-3,  # floor for the 2D data results
184        'qx_max'    : 0.5,
185        #'colormap'  : 'gist_ncar',
186        'colormap'  : 'nipy_spectral',
187        #'colormap'  : 'jet',
188    }
189
190    # Generate the RST file and the figure.  Order doesn't matter.
191    gen_docs(model_info)
192    make_figure(model_info, opts)
193
194if __name__ == "__main__":
195    process_model(sys.argv[1])
Note: See TracBrowser for help on using the repository browser.