Changes in / [8064d5e:4057e06] in sasmodels
- Files:
-
- 6 added
- 6 deleted
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
explore/beta/sasfit_compare.py
r2a12351b r119073a 505 505 } 506 506 507 Q, IQ = load_sasfit(data_file(' richard_test.txt'))508 Q, IQSD = load_sasfit(data_file(' richard_test2.txt'))509 Q, IQBD = load_sasfit(data_file(' richard_test3.txt'))507 Q, IQ = load_sasfit(data_file('sasfit_sphere_schulz_IQD.txt')) 508 Q, IQSD = load_sasfit(data_file('sasfit_sphere_schulz_IQSD.txt')) 509 Q, IQBD = load_sasfit(data_file('sasfit_sphere_schulz_IQBD.txt')) 510 510 target = Theory(Q=Q, F1=None, F2=None, P=IQ, S=None, I=IQSD, Seff=None, Ibeta=IQBD) 511 511 actual = sphere_r(Q, norm="sasfit", **pars) … … 526 526 } 527 527 528 Q, IQ = load_sasfit(data_file(' richard_test4.txt'))529 Q, IQSD = load_sasfit(data_file(' richard_test5.txt'))530 Q, IQBD = load_sasfit(data_file(' richard_test6.txt'))528 Q, IQ = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQD.txt')) 529 Q, IQSD = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQSD.txt')) 530 Q, IQBD = load_sasfit(data_file('sasfit_ellipsoid_shulz_IQBD.txt')) 531 531 target = Theory(Q=Q, F1=None, F2=None, P=IQ, S=None, I=IQSD, Seff=None, Ibeta=IQBD) 532 532 actual = ellipsoid_pe(Q, norm="sasfit", **pars) -
sasmodels/jitter.py
r7d97437 r275511c 1 1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 2 3 """ 3 4 Jitter Explorer … … 5 6 6 7 Application to explore orientation angle and angular dispersity. 8 9 From the command line:: 10 11 # Show docs 12 python -m sasmodels.jitter --help 13 14 # Guyou projection jitter, uniform over 20 degree theta and 10 in phi 15 python -m sasmodels.jitter --projection=guyou --dist=uniform --jitter=20,10,0 16 17 From a jupyter cell:: 18 19 import ipyvolume as ipv 20 from sasmodels import jitter 21 import importlib; importlib.reload(jitter) 22 jitter.set_plotter("ipv") 23 24 size = (10, 40, 100) 25 view = (20, 0, 0) 26 27 #size = (15, 15, 100) 28 #view = (60, 60, 0) 29 30 dview = (0, 0, 0) 31 #dview = (5, 5, 0) 32 #dview = (15, 180, 0) 33 #dview = (180, 15, 0) 34 35 projection = 'equirectangular' 36 #projection = 'azimuthal_equidistance' 37 #projection = 'guyou' 38 #projection = 'sinusoidal' 39 #projection = 'azimuthal_equal_area' 40 41 dist = 'uniform' 42 #dist = 'gaussian' 43 44 jitter.run(size=size, view=view, jitter=dview, dist=dist, projection=projection) 45 #filename = projection+('_theta' if dview[0] == 180 else '_phi' if dview[1] == 180 else '') 46 #ipv.savefig(filename+'.png') 7 47 """ 8 48 from __future__ import division, print_function … … 10 50 import argparse 11 51 12 try: # CRUFT: travis-ci does not support mpl_toolkits.mplot3d13 import mpl_toolkits.mplot3d # Adds projection='3d' option to subplot14 except ImportError:15 pass16 17 import matplotlib as mpl18 import matplotlib.pyplot as plt19 from matplotlib.widgets import Slider20 52 import numpy as np 21 53 from numpy import pi, cos, sin, sqrt, exp, degrees, radians 22 54 23 def draw_beam(axes, view=(0, 0) ):55 def draw_beam(axes, view=(0, 0), alpha=0.5, steps=25): 24 56 """ 25 57 Draw the beam going from source at (0, 0, 1) to detector at (0, 0, -1) 26 58 """ 27 59 #axes.plot([0,0],[0,0],[1,-1]) 28 #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=0.8) 29 30 steps = 25 60 #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=alpha) 61 31 62 u = np.linspace(0, 2 * np.pi, steps) 32 v = np.linspace(-1, 1, steps)63 v = np.linspace(-1, 1, 2) 33 64 34 65 r = 0.02 … … 42 73 points = Rz(phi)*Ry(theta)*points 43 74 x, y, z = [v.reshape(shape) for v in points] 44 45 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='y', alpha=0.5) 75 axes.plot_surface(x, y, z, color='yellow', alpha=alpha) 76 77 # TODO: draw endcaps on beam 78 ## Drawing tiny balls on the end will work 79 #draw_sphere(axes, radius=0.02, center=(0, 0, 1.3), color='yellow', alpha=alpha) 80 #draw_sphere(axes, radius=0.02, center=(0, 0, -1.3), color='yellow', alpha=alpha) 81 ## The following does not work 82 #triangles = [(0, i+1, i+2) for i in range(steps-2)] 83 #x_cap, y_cap = x[:, 0], y[:, 0] 84 #for z_cap in z[:, 0], z[:, -1]: 85 # axes.plot_trisurf(x_cap, y_cap, z_cap, triangles, 86 # color='yellow', alpha=alpha) 87 46 88 47 89 def draw_ellipsoid(axes, size, view, jitter, steps=25, alpha=1): … … 55 97 x, y, z = transform_xyz(view, jitter, x, y, z) 56 98 57 axes.plot_surface(x, y, z, rstride=4, cstride=4,color='w', alpha=alpha)99 axes.plot_surface(x, y, z, color='w', alpha=alpha) 58 100 59 101 draw_labels(axes, view, jitter, [ … … 124 166 return atoms 125 167 126 def draw_parallelepiped(axes, size, view, jitter, steps=None, alpha=1): 168 def draw_box(axes, size, view): 169 a, b, c = size 170 x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1]) 171 y = b*np.array([+1, +1, -1, -1, +1, +1, -1, -1]) 172 z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1]) 173 x, y, z = transform_xyz(view, None, x, y, z) 174 def draw(i, j): 175 axes.plot([x[i],x[j]], [y[i], y[j]], [z[i], z[j]], color='black') 176 draw(0, 1) 177 draw(0, 2) 178 draw(0, 3) 179 draw(7, 4) 180 draw(7, 5) 181 draw(7, 6) 182 183 def draw_parallelepiped(axes, size, view, jitter, steps=None, 184 color=(0.6, 1.0, 0.6), alpha=1): 127 185 """Draw a parallelepiped.""" 128 186 a, b, c = size … … 142 200 143 201 x, y, z = transform_xyz(view, jitter, x, y, z) 144 axes.plot_trisurf(x, y, triangles=tri, Z=z, color='w', alpha=alpha) 145 146 # Draw pink face on box. 202 axes.plot_trisurf(x, y, triangles=tri, Z=z, color=color, alpha=alpha, 203 linewidth=0) 204 205 # Colour the c+ face of the box. 147 206 # Since I can't control face color, instead draw a thin box situated just 148 207 # in front of the "c+" face. Use the c face so that rotations about psi 149 208 # rotate that face. 150 if 1: 209 if 0: 210 color = (1, 0.6, 0.6) # pink 151 211 x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1]) 152 212 y = b*np.array([+1, +1, -1, -1, +1, +1, -1, -1]) 153 213 z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1]) 154 214 x, y, z = transform_xyz(view, jitter, x, y, abs(z)+0.001) 155 axes.plot_trisurf(x, y, triangles=tri, Z=z, color= [1, 0.6, 0.6], alpha=alpha)215 axes.plot_trisurf(x, y, triangles=tri, Z=z, color=color, alpha=alpha) 156 216 157 217 draw_labels(axes, view, jitter, [ … … 164 224 ]) 165 225 166 def draw_sphere(axes, radius= 10., steps=100):226 def draw_sphere(axes, radius=0.5, steps=25, center=(0,0,0), color='w', alpha=1.): 167 227 """Draw a sphere""" 168 228 u = np.linspace(0, 2 * np.pi, steps) 169 229 v = np.linspace(0, np.pi, steps) 170 230 171 x = radius * np.outer(np.cos(u), np.sin(v)) 172 y = radius * np.outer(np.sin(u), np.sin(v)) 173 z = radius * np.outer(np.ones(np.size(u)), np.cos(v)) 174 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='w') 175 176 def draw_jitter(axes, view, jitter, dist='gaussian', size=(0.1, 0.4, 1.0), 177 draw_shape=draw_parallelepiped): 231 x = radius * np.outer(np.cos(u), np.sin(v)) + center[0] 232 y = radius * np.outer(np.sin(u), np.sin(v)) + center[1] 233 z = radius * np.outer(np.ones(np.size(u)), np.cos(v)) + center[2] 234 axes.plot_surface(x, y, z, color=color, alpha=alpha) 235 #axes.plot_wireframe(x, y, z) 236 237 def draw_axes(axes, origin=(-1, -1, -1), length=(2, 2, 2)): 238 x, y, z = origin 239 dx, dy, dz = length 240 axes.plot([x, x+dx], [y, y], [z, z], color='black') 241 axes.plot([x, x], [y, y+dy], [z, z], color='black') 242 axes.plot([x, x], [y, y], [z, z+dz], color='black') 243 244 def draw_person_on_sphere(axes, view, height=0.5, radius=0.5): 245 limb_offset = height * 0.05 246 head_radius = height * 0.10 247 head_height = height - head_radius 248 neck_length = head_radius * 0.50 249 shoulder_height = height - 2*head_radius - neck_length 250 torso_length = shoulder_height * 0.55 251 torso_radius = torso_length * 0.30 252 leg_length = shoulder_height - torso_length 253 arm_length = torso_length * 0.90 254 255 def _draw_part(x, z): 256 y = np.zeros_like(x) 257 xp, yp, zp = transform_xyz(view, None, x, y, z + radius) 258 axes.plot(xp, yp, zp, color='k') 259 260 # circle for head 261 u = np.linspace(0, 2 * np.pi, 40) 262 x = head_radius * np.cos(u) 263 z = head_radius * np.sin(u) + head_height 264 _draw_part(x, z) 265 266 # rectangle for body 267 x = np.array([-torso_radius, torso_radius, torso_radius, -torso_radius, -torso_radius]) 268 z = np.array([0., 0, torso_length, torso_length, 0]) + leg_length 269 _draw_part(x, z) 270 271 # arms 272 x = np.array([-torso_radius - limb_offset, -torso_radius - limb_offset, -torso_radius]) 273 z = np.array([shoulder_height - arm_length, shoulder_height, shoulder_height]) 274 _draw_part(x, z) 275 _draw_part(-x, z) 276 277 # legs 278 x = np.array([-torso_radius + limb_offset, -torso_radius + limb_offset]) 279 z = np.array([0, leg_length]) 280 _draw_part(x, z) 281 _draw_part(-x, z) 282 283 limits = [-radius-height, radius+height] 284 axes.set_xlim(limits) 285 axes.set_ylim(limits) 286 axes.set_zlim(limits) 287 axes.set_axis_off() 288 289 def draw_jitter(axes, view, jitter, dist='gaussian', 290 size=(0.1, 0.4, 1.0), 291 draw_shape=draw_parallelepiped, 292 projection='equirectangular', 293 alpha=0.8, 294 views=None): 178 295 """ 179 296 Represent jitter as a set of shapes at different orientations. 180 297 """ 298 project, project_weight = get_projection(projection) 299 181 300 # set max diagonal to 0.95 182 301 scale = 0.95/sqrt(sum(v**2 for v in size)) 183 302 size = tuple(scale*v for v in size) 184 303 185 #np.random.seed(10)186 #cloud = np.random.randn(10,3)187 cloud = [188 [-1, -1, -1],189 [-1, -1, +0],190 [-1, -1, +1],191 [-1, +0, -1],192 [-1, +0, +0],193 [-1, +0, +1],194 [-1, +1, -1],195 [-1, +1, +0],196 [-1, +1, +1],197 [+0, -1, -1],198 [+0, -1, +0],199 [+0, -1, +1],200 [+0, +0, -1],201 [+0, +0, +0],202 [+0, +0, +1],203 [+0, +1, -1],204 [+0, +1, +0],205 [+0, +1, +1],206 [+1, -1, -1],207 [+1, -1, +0],208 [+1, -1, +1],209 [+1, +0, -1],210 [+1, +0, +0],211 [+1, +0, +1],212 [+1, +1, -1],213 [+1, +1, +0],214 [+1, +1, +1],215 ]216 304 dtheta, dphi, dpsi = jitter 217 if dtheta == 0: 218 cloud = [v for v in cloud if v[0] == 0] 219 if dphi == 0: 220 cloud = [v for v in cloud if v[1] == 0] 221 if dpsi == 0: 222 cloud = [v for v in cloud if v[2] == 0] 223 draw_shape(axes, size, view, [0, 0, 0], steps=100, alpha=0.8) 224 scale = {'gaussian':1, 'rectangle':1/sqrt(3), 'uniform':1/3}[dist] 225 for point in cloud: 226 delta = [scale*dtheta*point[0], scale*dphi*point[1], scale*dpsi*point[2]] 227 draw_shape(axes, size, view, delta, alpha=0.8) 305 base = {'gaussian':3, 'rectangle':sqrt(3), 'uniform':1}[dist] 306 def steps(delta): 307 if views is None: 308 n = max(3, min(25, 2*int(base*delta/5))) 309 else: 310 n = views 311 return base*delta*np.linspace(-1, 1, n) if delta > 0 else [0.] 312 for theta in steps(dtheta): 313 for phi in steps(dphi): 314 for psi in steps(dpsi): 315 w = project_weight(theta, phi, 1.0, 1.0) 316 if w > 0: 317 dview = project(theta, phi, psi) 318 draw_shape(axes, size, view, dview, alpha=alpha) 228 319 for v in 'xyz': 229 320 a, b, c = size 230 321 lim = np.sqrt(a**2 + b**2 + c**2) 231 322 getattr(axes, 'set_'+v+'lim')([-lim, lim]) 232 getattr(axes, v+'axis').label.set_text(v)323 #getattr(axes, v+'axis').label.set_text(v) 233 324 234 325 PROJECTIONS = [ … … 238 329 'azimuthal_equal_area', 239 330 ] 240 def draw_mesh(axes, view, jitter, radius=1.2, n=11, dist='gaussian', 241 projection='equirectangular'): 242 """ 243 Draw the dispersion mesh showing the theta-phi orientations at which 244 the model will be evaluated. 245 331 def get_projection(projection): 332 333 """ 246 334 jitter projections 247 335 <https://en.wikipedia.org/wiki/List_of_map_projections> … … 299 387 # TODO: try Kent distribution instead of a gaussian warped by projection 300 388 301 dist_x = np.linspace(-1, 1, n)302 weights = np.ones_like(dist_x)303 if dist == 'gaussian':304 dist_x *= 3305 weights = exp(-0.5*dist_x**2)306 elif dist == 'rectangle':307 # Note: uses sasmodels ridiculous definition of rectangle width308 dist_x *= sqrt(3)309 elif dist == 'uniform':310 pass311 else:312 raise ValueError("expected dist to be gaussian, rectangle or uniform")313 314 389 if projection == 'equirectangular': #define PROJECTION 1 315 def _rotate(theta_i, phi_j): 316 return Rx(phi_j)*Ry(theta_i) 390 def _project(theta_i, phi_j, psi): 391 latitude, longitude = theta_i, phi_j 392 return latitude, longitude, psi 393 #return Rx(phi_j)*Ry(theta_i) 317 394 def _weight(theta_i, phi_j, w_i, w_j): 318 395 return w_i*w_j*abs(cos(radians(theta_i))) 319 396 elif projection == 'sinusoidal': #define PROJECTION 2 320 def _ rotate(theta_i, phi_j):397 def _project(theta_i, phi_j, psi): 321 398 latitude = theta_i 322 399 scale = cos(radians(latitude)) 323 400 longitude = phi_j/scale if abs(phi_j) < abs(scale)*180 else 0 324 401 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 325 return Rx(longitude)*Ry(latitude) 326 def _weight(theta_i, phi_j, w_i, w_j): 402 return latitude, longitude, psi 403 #return Rx(longitude)*Ry(latitude) 404 def _project(theta_i, phi_j, w_i, w_j): 327 405 latitude = theta_i 328 406 scale = cos(radians(latitude)) … … 330 408 return active*w_i*w_j 331 409 elif projection == 'guyou': #define PROJECTION 3 (eventually?) 332 def _ rotate(theta_i, phi_j):410 def _project(theta_i, phi_j, psi): 333 411 from .guyou import guyou_invert 334 412 #latitude, longitude = guyou_invert([theta_i], [phi_j]) 335 413 longitude, latitude = guyou_invert([phi_j], [theta_i]) 336 return Rx(longitude[0])*Ry(latitude[0]) 414 return latitude, longitude, psi 415 #return Rx(longitude[0])*Ry(latitude[0]) 337 416 def _weight(theta_i, phi_j, w_i, w_j): 338 417 return w_i*w_j 339 elif projection == 'azimuthal_equidistance': # Note: Rz Ry, not Rx Ry 340 def _rotate(theta_i, phi_j): 418 elif projection == 'azimuthal_equidistance': 419 # Note that calculates angles for Rz Ry rather than Rx Ry 420 def _project(theta_i, phi_j, psi): 341 421 latitude = sqrt(theta_i**2 + phi_j**2) 342 422 longitude = degrees(np.arctan2(phi_j, theta_i)) 343 423 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 344 return Rz(longitude)*Ry(latitude) 424 return latitude, longitude, psi-longitude, 'zyz' 425 #R = Rz(longitude)*Ry(latitude)*Rz(psi) 426 #return R_to_xyz(R) 427 #return Rz(longitude)*Ry(latitude) 345 428 def _weight(theta_i, phi_j, w_i, w_j): 346 429 # Weighting for each point comes from the integral: … … 376 459 return weight*w_i*w_j if latitude < 180 else 0 377 460 elif projection == 'azimuthal_equal_area': 378 def _rotate(theta_i, phi_j): 461 # Note that calculates angles for Rz Ry rather than Rx Ry 462 def _project(theta_i, phi_j, psi): 379 463 radius = min(1, sqrt(theta_i**2 + phi_j**2)/180) 380 464 latitude = 180-degrees(2*np.arccos(radius)) 381 465 longitude = degrees(np.arctan2(phi_j, theta_i)) 382 466 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 383 return Rz(longitude)*Ry(latitude) 467 return latitude, longitude, psi, "zyz" 468 #R = Rz(longitude)*Ry(latitude)*Rz(psi) 469 #return R_to_xyz(R) 470 #return Rz(longitude)*Ry(latitude) 384 471 def _weight(theta_i, phi_j, w_i, w_j): 385 472 latitude = sqrt(theta_i**2 + phi_j**2) … … 389 476 raise ValueError("unknown projection %r"%projection) 390 477 478 return _project, _weight 479 480 def R_to_xyz(R): 481 """ 482 Return phi, theta, psi Tait-Bryan angles corresponding to the given rotation matrix. 483 484 Extracting Euler Angles from a Rotation Matrix 485 Mike Day, Insomniac Games 486 https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf 487 Based on: Shoemakeâs "Euler Angle Conversion", Graphics Gems IV, pp. 222-229 488 """ 489 phi = np.arctan2(R[1, 2], R[2, 2]) 490 theta = np.arctan2(-R[0, 2], np.sqrt(R[0, 0]**2 + R[0, 1]**2)) 491 psi = np.arctan2(R[0, 1], R[0, 0]) 492 return np.degrees(phi), np.degrees(theta), np.degrees(psi) 493 494 def draw_mesh(axes, view, jitter, radius=1.2, n=11, dist='gaussian', 495 projection='equirectangular'): 496 """ 497 Draw the dispersion mesh showing the theta-phi orientations at which 498 the model will be evaluated. 499 """ 500 501 _project, _weight = get_projection(projection) 502 def _rotate(theta, phi, z): 503 dview = _project(theta, phi, 0.) 504 if len(dview) == 4: # hack for zyz coords 505 return Rz(dview[1])*Ry(dview[0])*z 506 else: 507 return Rx(dview[1])*Ry(dview[0])*z 508 509 510 dist_x = np.linspace(-1, 1, n) 511 weights = np.ones_like(dist_x) 512 if dist == 'gaussian': 513 dist_x *= 3 514 weights = exp(-0.5*dist_x**2) 515 elif dist == 'rectangle': 516 # Note: uses sasmodels ridiculous definition of rectangle width 517 dist_x *= sqrt(3) 518 elif dist == 'uniform': 519 pass 520 else: 521 raise ValueError("expected dist to be gaussian, rectangle or uniform") 522 391 523 # mesh in theta, phi formed by rotating z 392 524 dtheta, dphi, dpsi = jitter 393 525 z = np.matrix([[0], [0], [radius]]) 394 points = np.hstack([_rotate(theta_i, phi_j )*z526 points = np.hstack([_rotate(theta_i, phi_j, z) 395 527 for theta_i in dtheta*dist_x 396 528 for phi_j in dphi*dist_x]) … … 470 602 Points are stored in a 3 x n numpy matrix, not a numpy array or tuple. 471 603 """ 472 dtheta, dphi, dpsi = jitter 473 points = Rx(dphi)*Ry(dtheta)*Rz(dpsi)*points 604 if jitter is None: 605 return points 606 # Hack to deal with the fact that azimuthal_equidistance uses euler angles 607 if len(jitter) == 4: 608 dtheta, dphi, dpsi, _ = jitter 609 points = Rz(dphi)*Ry(dtheta)*Rz(dpsi)*points 610 else: 611 dtheta, dphi, dpsi = jitter 612 points = Rx(dphi)*Ry(dtheta)*Rz(dpsi)*points 474 613 return points 475 614 … … 481 620 """ 482 621 theta, phi, psi = view 483 points = Rz(phi)*Ry(theta)*Rz(psi)*points 622 points = Rz(phi)*Ry(theta)*Rz(psi)*points # viewing angle 623 #points = Rz(psi)*Ry(pi/2-theta)*Rz(phi)*points # 1-D integration angles 624 #points = Rx(phi)*Ry(theta)*Rz(psi)*points # angular dispersion angle 484 625 return points 626 627 def orient_relative_to_beam_quaternion(view, points): 628 """ 629 Apply the view transform to a set of points. 630 631 Points are stored in a 3 x n numpy matrix, not a numpy array or tuple. 632 633 This variant uses quaternions rather than rotation matrices for the 634 computation. It works but it is not used because it doesn't solve 635 any problems. The challenge of mapping theta/phi/psi to SO(3) does 636 not disappear by calculating the transform differently. 637 """ 638 theta, phi, psi = view 639 x, y, z = [1, 0, 0], [0, 1, 0], [0, 0, 1] 640 q = Quaternion(1, [0, 0, 0]) 641 ## Compose a rotation about the three axes by rotating 642 ## the unit vectors before applying the rotation. 643 #q = Quaternion.from_angle_axis(theta, q.rot(x)) * q 644 #q = Quaternion.from_angle_axis(phi, q.rot(y)) * q 645 #q = Quaternion.from_angle_axis(psi, q.rot(z)) * q 646 ## The above turns out to be equivalent to reversing 647 ## the order of application, so ignore it and use below. 648 q = q * Quaternion.from_angle_axis(theta, x) 649 q = q * Quaternion.from_angle_axis(phi, y) 650 q = q * Quaternion.from_angle_axis(psi, z) 651 ## Reverse the order by post-multiply rather than pre-multiply 652 #q = Quaternion.from_angle_axis(theta, x) * q 653 #q = Quaternion.from_angle_axis(phi, y) * q 654 #q = Quaternion.from_angle_axis(psi, z) * q 655 #print("axes psi", q.rot(np.matrix([x, y, z]).T)) 656 return q.rot(points) 657 #orient_relative_to_beam = orient_relative_to_beam_quaternion 658 659 # Simple stand-alone quaternion class 660 import numpy as np 661 from copy import copy 662 class Quaternion(object): 663 def __init__(self, w, r): 664 self.w = w 665 self.r = np.asarray(r, dtype='d') 666 @staticmethod 667 def from_angle_axis(theta, r): 668 theta = np.radians(theta)/2 669 r = np.asarray(r) 670 w = np.cos(theta) 671 r = np.sin(theta)*r/np.dot(r,r) 672 return Quaternion(w, r) 673 def __mul__(self, other): 674 if isinstance(other, Quaternion): 675 w = self.w*other.w - np.dot(self.r, other.r) 676 r = self.w*other.r + other.w*self.r + np.cross(self.r, other.r) 677 return Quaternion(w, r) 678 def rot(self, v): 679 v = np.asarray(v).T 680 use_transpose = (v.shape[-1] != 3) 681 if use_transpose: v = v.T 682 v = v + np.cross(2*self.r, np.cross(self.r, v) + self.w*v) 683 #v = v + 2*self.w*np.cross(self.r, v) + np.cross(2*self.r, np.cross(self.r, v)) 684 if use_transpose: v = v.T 685 return v.T 686 def conj(self): 687 return Quaternion(self.w, -self.r) 688 def inv(self): 689 return self.conj()/self.norm()**2 690 def norm(self): 691 return np.sqrt(self.w**2 + np.sum(self.r**2)) 692 def __str__(self): 693 return "%g%+gi%+gj%+gk"%(self.w, self.r[0], self.r[1], self.r[2]) 694 def test_qrot(): 695 # Define rotation of 60 degrees around an axis in y-z that is 60 degrees from y. 696 # The rotation axis is determined by rotating the point [0, 1, 0] about x. 697 ax = Quaternion.from_angle_axis(60, [1, 0, 0]).rot([0, 1, 0]) 698 q = Quaternion.from_angle_axis(60, ax) 699 # Set the point to be rotated, and its expected rotated position. 700 p = [1, -1, 2] 701 target = [(10+4*np.sqrt(3))/8, (1+2*np.sqrt(3))/8, (14-3*np.sqrt(3))/8] 702 #print(q, q.rot(p) - target) 703 assert max(abs(q.rot(p) - target)) < 1e-14 704 #test_qrot() 705 #import sys; sys.exit() 485 706 486 707 # translate between number of dimension of dispersity and the number of … … 556 777 vmin = vmax*10**-7 557 778 #vmin, vmax = clipped_range(Iqxy, portion=portion, mode='top') 779 #vmin, vmax = Iqxy.min(), Iqxy.max() 558 780 #print("range",(vmin,vmax)) 559 781 #qx, qy = np.meshgrid(qx, qy) 560 782 if 0: 783 from matplotlib import cm 561 784 level = np.asarray(255*(Iqxy - vmin)/(vmax - vmin), 'i') 562 785 level[level < 0] = 0 563 786 colors = plt.get_cmap()(level) 564 axes.plot_surface(qx, qy, -1.1, rstride=1, cstride=1, facecolors=colors) 787 #colors = cm.coolwarm(level) 788 #colors = cm.gist_yarg(level) 789 #colors = cm.Wistia(level) 790 colors[level<=0, 3] = 0. # set floor to transparent 791 x, y = np.meshgrid(qx/qx.max(), qy/qy.max()) 792 axes.plot_surface(x, y, -1.1*np.ones_like(x), facecolors=colors) 565 793 elif 1: 566 794 axes.contourf(qx/qx.max(), qy/qy.max(), Iqxy, zdir='z', offset=-1.1, … … 692 920 } 693 921 922 694 923 def run(model_name='parallelepiped', size=(10, 40, 100), 924 view=(0, 0, 0), jitter=(0, 0, 0), 695 925 dist='gaussian', mesh=30, 696 926 projection='equirectangular'): … … 702 932 703 933 *size* gives the dimensions (a, b, c) of the shape. 934 935 *view* gives the initial view (theta, phi, psi) of the shape. 936 937 *view* gives the initial jitter (dtheta, dphi, dpsi) of the shape. 704 938 705 939 *dist* is the type of dispersition: gaussian, rectangle, or uniform. … … 721 955 calculator, size = select_calculator(model_name, n=150, size=size) 722 956 draw_shape = DRAW_SHAPES.get(model_name, draw_parallelepiped) 957 #draw_shape = draw_fcc 723 958 724 959 ## uncomment to set an independent the colour range for every view … … 726 961 calculator.limits = None 727 962 728 ## initial view 729 #theta, dtheta = 70., 10. 730 #phi, dphi = -45., 3. 731 #psi, dpsi = -45., 3. 732 theta, phi, psi = 0, 0, 0 733 dtheta, dphi, dpsi = 0, 0, 0 734 963 PLOT_ENGINE(calculator, draw_shape, size, view, jitter, dist, mesh, projection) 964 965 def mpl_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection): 966 # Note: travis-ci does not support mpl_toolkits.mplot3d, but this shouldn't be 967 # an issue since we are lazy-loading the package on a path that isn't tested. 968 import mpl_toolkits.mplot3d # Adds projection='3d' option to subplot 969 import matplotlib as mpl 970 import matplotlib.pyplot as plt 971 from matplotlib.widgets import Slider 972 735 973 ## create the plot window 736 974 #plt.hold(True) … … 747 985 pass 748 986 987 749 988 # CRUFT: use axisbg instead of facecolor for matplotlib<2 750 989 facecolor_prop = 'facecolor' if mpl.__version__ > '2' else 'axisbg' … … 762 1001 axes_dphi = plt.axes([0.75, 0.1, 0.15, 0.04], **props) 763 1002 axes_dpsi = plt.axes([0.75, 0.05, 0.15, 0.04], **props) 1003 764 1004 # Note: using ridiculous definition of rectangle distribution, whose width 765 1005 # in sasmodels is sqrt(3) times the given width. Divide by sqrt(3) to keep 766 1006 # the maximum width to 90. 767 1007 dlimit = DIST_LIMITS[dist] 768 sdtheta = Slider(axes_dtheta, 'dTheta', 0, 2*dlimit, valinit=dtheta) 769 sdphi = Slider(axes_dphi, 'dPhi', 0, 2*dlimit, valinit=dphi) 770 sdpsi = Slider(axes_dpsi, 'dPsi', 0, 2*dlimit, valinit=dpsi) 771 1008 sdtheta = Slider(axes_dtheta, u'ÎΞ', 0, 2*dlimit, valinit=0) 1009 sdphi = Slider(axes_dphi, u'ÎÏ', 0, 2*dlimit, valinit=0) 1010 sdpsi = Slider(axes_dpsi, u'ÎÏ', 0, 2*dlimit, valinit=0) 1011 1012 ## initial view and jitter 1013 theta, phi, psi = view 1014 stheta.set_val(theta) 1015 sphi.set_val(phi) 1016 spsi.set_val(psi) 1017 dtheta, dphi, dpsi = jitter 1018 sdtheta.set_val(dtheta) 1019 sdphi.set_val(dphi) 1020 sdpsi.set_val(dpsi) 772 1021 773 1022 ## callback to draw the new view … … 777 1026 # set small jitter as 0 if multiple pd dims 778 1027 dims = sum(v > 0 for v in jitter) 779 limit = [0, 0.5, 5 ][dims]1028 limit = [0, 0.5, 5, 5][dims] 780 1029 jitter = [0 if v < limit else v for v in jitter] 781 1030 axes.cla() 782 draw_beam(axes, (0, 0)) 783 draw_jitter(axes, view, jitter, dist=dist, size=size, draw_shape=draw_shape) 784 #draw_jitter(axes, view, (0,0,0)) 1031 1032 ## Visualize as person on globe 1033 #draw_sphere(axes) 1034 #draw_person_on_sphere(axes, view) 1035 1036 ## Move beam instead of shape 1037 #draw_beam(axes, -view[:2]) 1038 #draw_jitter(axes, (0,0,0), (0,0,0), views=3) 1039 1040 ## Move shape and draw scattering 1041 draw_beam(axes, (0, 0), alpha=1.) 1042 draw_jitter(axes, view, jitter, dist=dist, size=size, alpha=1., 1043 draw_shape=draw_shape, projection=projection, views=3) 785 1044 draw_mesh(axes, view, jitter, dist=dist, n=mesh, projection=projection) 786 1045 draw_scattering(calculator, axes, view, jitter, dist=dist) 1046 787 1047 plt.gcf().canvas.draw() 788 1048 … … 800 1060 ## go interactive 801 1061 plt.show() 1062 1063 1064 def map_colors(z, kw): 1065 from matplotlib import cm 1066 1067 cmap = kw.pop('cmap', cm.coolwarm) 1068 alpha = kw.pop('alpha', None) 1069 vmin = kw.pop('vmin', z.min()) 1070 vmax = kw.pop('vmax', z.max()) 1071 c = kw.pop('c', None) 1072 color = kw.pop('color', c) 1073 if color is None: 1074 znorm = ((z - vmin) / (vmax - vmin)).clip(0, 1) 1075 color = cmap(znorm) 1076 elif isinstance(color, np.ndarray) and color.shape == z.shape: 1077 color = cmap(color) 1078 if alpha is None: 1079 if isinstance(color, np.ndarray): 1080 color = color[..., :3] 1081 else: 1082 color[..., 3] = alpha 1083 kw['color'] = color 1084 1085 def make_vec(*args): 1086 #return [np.asarray(v, 'd').flatten() for v in args] 1087 return [np.asarray(v, 'd') for v in args] 1088 1089 def make_image(z, kw): 1090 import PIL.Image 1091 from matplotlib import cm 1092 1093 cmap = kw.pop('cmap', cm.coolwarm) 1094 1095 znorm = (z-z.min())/z.ptp() 1096 c = cmap(znorm) 1097 c = c[..., :3] 1098 rgb = np.asarray(c*255, 'u1') 1099 image = PIL.Image.fromarray(rgb, mode='RGB') 1100 return image 1101 1102 1103 _IPV_MARKERS = { 1104 'o': 'sphere', 1105 } 1106 _IPV_COLORS = { 1107 'w': 'white', 1108 'k': 'black', 1109 'c': 'cyan', 1110 'm': 'magenta', 1111 'y': 'yellow', 1112 'r': 'red', 1113 'g': 'green', 1114 'b': 'blue', 1115 } 1116 def ipv_fix_color(kw): 1117 alpha = kw.pop('alpha', None) 1118 color = kw.get('color', None) 1119 if isinstance(color, str): 1120 color = _IPV_COLORS.get(color, color) 1121 kw['color'] = color 1122 if alpha is not None: 1123 color = kw['color'] 1124 #TODO: convert color to [r, g, b, a] if not already 1125 if isinstance(color, (tuple, list)): 1126 if len(color) == 3: 1127 color = (color[0], color[1], color[2], alpha) 1128 else: 1129 color = (color[0], color[1], color[2], alpha*color[3]) 1130 color = np.array(color) 1131 if isinstance(color, np.ndarray) and color.shape[-1] == 4: 1132 color[..., 3] = alpha 1133 kw['color'] = color 1134 1135 def ipv_set_transparency(kw, obj): 1136 color = kw.get('color', None) 1137 if (isinstance(color, np.ndarray) 1138 and color.shape[-1] == 4 1139 and (color[..., 3] != 1.0).any()): 1140 obj.material.transparent = True 1141 obj.material.side = "FrontSide" 1142 1143 def ipv_axes(): 1144 import ipyvolume as ipv 1145 1146 class Plotter: 1147 # transparency can be achieved by setting the following: 1148 # mesh.color = [r, g, b, alpha] 1149 # mesh.material.transparent = True 1150 # mesh.material.side = "FrontSide" 1151 # smooth(ish) rotation can be achieved by setting: 1152 # slide.continuous_update = True 1153 # figure.animation = 0. 1154 # mesh.material.x = x 1155 # mesh.material.y = y 1156 # mesh.material.z = z 1157 # maybe need to synchronize update of x/y/z to avoid shimmy when moving 1158 def plot(self, x, y, z, **kw): 1159 ipv_fix_color(kw) 1160 x, y, z = make_vec(x, y, z) 1161 ipv.plot(x, y, z, **kw) 1162 def plot_surface(self, x, y, z, **kw): 1163 facecolors = kw.pop('facecolors', None) 1164 if facecolors is not None: 1165 kw['color'] = facecolors 1166 ipv_fix_color(kw) 1167 x, y, z = make_vec(x, y, z) 1168 h = ipv.plot_surface(x, y, z, **kw) 1169 ipv_set_transparency(kw, h) 1170 #h.material.side = "DoubleSide" 1171 return h 1172 def plot_trisurf(self, x, y, triangles=None, Z=None, **kw): 1173 kw.pop('linewidth', None) 1174 ipv_fix_color(kw) 1175 x, y, z = make_vec(x, y, Z) 1176 if triangles is not None: 1177 triangles = np.asarray(triangles) 1178 h = ipv.plot_trisurf(x, y, z, triangles=triangles, **kw) 1179 ipv_set_transparency(kw, h) 1180 return h 1181 def scatter(self, x, y, z, **kw): 1182 x, y, z = make_vec(x, y, z) 1183 map_colors(z, kw) 1184 marker = kw.get('marker', None) 1185 kw['marker'] = _IPV_MARKERS.get(marker, marker) 1186 h = ipv.scatter(x, y, z, **kw) 1187 ipv_set_transparency(kw, h) 1188 return h 1189 def contourf(self, x, y, v, zdir='z', offset=0, levels=None, **kw): 1190 # Don't use contour for now (although we might want to later) 1191 self.pcolor(x, y, v, zdir='z', offset=offset, **kw) 1192 def pcolor(self, x, y, v, zdir='z', offset=0, **kw): 1193 x, y, v = make_vec(x, y, v) 1194 image = make_image(v, kw) 1195 xmin, xmax = x.min(), x.max() 1196 ymin, ymax = y.min(), y.max() 1197 x = np.array([[xmin, xmax], [xmin, xmax]]) 1198 y = np.array([[ymin, ymin], [ymax, ymax]]) 1199 z = x*0 + offset 1200 u = np.array([[0., 1], [0, 1]]) 1201 v = np.array([[0., 0], [1, 1]]) 1202 h = ipv.plot_mesh(x, y, z, u=u, v=v, texture=image, wireframe=False) 1203 ipv_set_transparency(kw, h) 1204 h.material.side = "DoubleSide" 1205 return h 1206 def text(self, *args, **kw): 1207 pass 1208 def set_xlim(self, limits): 1209 ipv.xlim(*limits) 1210 def set_ylim(self, limits): 1211 ipv.ylim(*limits) 1212 def set_zlim(self, limits): 1213 ipv.zlim(*limits) 1214 def set_axes_on(self): 1215 ipv.style.axis_on() 1216 def set_axis_off(self): 1217 ipv.style.axes_off() 1218 return Plotter() 1219 1220 def ipv_plot(calculator, draw_shape, size, view, jitter, dist, mesh, projection): 1221 import ipywidgets as widgets 1222 import ipyvolume as ipv 1223 1224 axes = ipv_axes() 1225 1226 def draw(view, jitter): 1227 camera = ipv.gcf().camera 1228 #print(ipv.gcf().__dict__.keys()) 1229 #print(dir(ipv.gcf())) 1230 ipv.figure(animation=0.) # no animation when updating object mesh 1231 1232 # set small jitter as 0 if multiple pd dims 1233 dims = sum(v > 0 for v in jitter) 1234 limit = [0, 0.5, 5, 5][dims] 1235 jitter = [0 if v < limit else v for v in jitter] 1236 1237 ## Visualize as person on globe 1238 #draw_beam(axes, (0, 0)) 1239 #draw_sphere(axes) 1240 #draw_person_on_sphere(axes, view) 1241 1242 ## Move beam instead of shape 1243 #draw_beam(axes, view=(-view[0], -view[1])) 1244 #draw_jitter(axes, view=(0,0,0), jitter=(0,0,0)) 1245 1246 ## Move shape and draw scattering 1247 draw_beam(axes, (0, 0), steps=25) 1248 draw_jitter(axes, view, jitter, dist=dist, size=size, alpha=1.0, 1249 draw_shape=draw_shape, projection=projection) 1250 draw_mesh(axes, view, jitter, dist=dist, n=mesh, radius=0.95, 1251 projection=projection) 1252 draw_scattering(calculator, axes, view, jitter, dist=dist) 1253 1254 draw_axes(axes, origin=(-1, -1, -1.1)) 1255 ipv.style.box_off() 1256 ipv.style.axes_off() 1257 ipv.xyzlabel(" ", " ", " ") 1258 1259 ipv.gcf().camera = camera 1260 ipv.show() 1261 1262 1263 trange, prange = (-180., 180., 1.), (-180., 180., 1.) 1264 dtrange, dprange = (0., 180., 1.), (0., 180., 1.) 1265 1266 ## Super simple interfaca, but uses non-ascii variable namese 1267 # Ξ Ï Ï ÎΞ ÎÏ ÎÏ 1268 #def update(**kw): 1269 # view = kw['Ξ'], kw['Ï'], kw['Ï'] 1270 # jitter = kw['ÎΞ'], kw['ÎÏ'], kw['ÎÏ'] 1271 # draw(view, jitter) 1272 #widgets.interact(update, Ξ=trange, Ï=prange, Ï=prange, ÎΞ=dtrange, ÎÏ=dprange, ÎÏ=dprange) 1273 1274 def update(theta, phi, psi, dtheta, dphi, dpsi): 1275 draw(view=(theta, phi, psi), jitter=(dtheta, dphi, dpsi)) 1276 1277 def slider(name, slice, init=0.): 1278 return widgets.FloatSlider( 1279 value=init, 1280 min=slice[0], 1281 max=slice[1], 1282 step=slice[2], 1283 description=name, 1284 disabled=False, 1285 #continuous_update=True, 1286 continuous_update=False, 1287 orientation='horizontal', 1288 readout=True, 1289 readout_format='.1f', 1290 ) 1291 theta = slider(u'Ξ', trange, view[0]) 1292 phi = slider(u'Ï', prange, view[1]) 1293 psi = slider(u'Ï', prange, view[2]) 1294 dtheta = slider(u'ÎΞ', dtrange, jitter[0]) 1295 dphi = slider(u'ÎÏ', dprange, jitter[1]) 1296 dpsi = slider(u'ÎÏ', dprange, jitter[2]) 1297 fields = { 1298 'theta': theta, 'phi': phi, 'psi': psi, 1299 'dtheta': dtheta, 'dphi': dphi, 'dpsi': dpsi, 1300 } 1301 ui = widgets.HBox([ 1302 widgets.VBox([theta, phi, psi]), 1303 widgets.VBox([dtheta, dphi, dpsi]) 1304 ]) 1305 1306 out = widgets.interactive_output(update, fields) 1307 display(ui, out) 1308 1309 1310 _ENGINES = { 1311 "matplotlib": mpl_plot, 1312 "mpl": mpl_plot, 1313 #"plotly": plotly_plot, 1314 "ipvolume": ipv_plot, 1315 "ipv": ipv_plot, 1316 } 1317 PLOT_ENGINE = _ENGINES["matplotlib"] 1318 def set_plotter(name): 1319 global PLOT_ENGINE 1320 PLOT_ENGINE = _ENGINES[name] 802 1321 803 1322 def main(): … … 811 1330 parser.add_argument('-s', '--size', type=str, default='10,40,100', 812 1331 help='a,b,c lengths') 1332 parser.add_argument('-v', '--view', type=str, default='0,0,0', 1333 help='initial view angles') 1334 parser.add_argument('-j', '--jitter', type=str, default='0,0,0', 1335 help='initial angular dispersion') 813 1336 parser.add_argument('-d', '--distribution', choices=DISTRIBUTIONS, 814 1337 default=DISTRIBUTIONS[0], … … 819 1342 help='oriented shape') 820 1343 opts = parser.parse_args() 821 size = tuple(int(v) for v in opts.size.split(',')) 822 run(opts.shape, size=size, 1344 size = tuple(float(v) for v in opts.size.split(',')) 1345 view = tuple(float(v) for v in opts.view.split(',')) 1346 jitter = tuple(float(v) for v in opts.jitter.split(',')) 1347 run(opts.shape, size=size, view=view, jitter=jitter, 823 1348 mesh=opts.mesh, dist=opts.distribution, 824 1349 projection=opts.projection) -
sasmodels/models/pearl_necklace.c
r99658f6 r9b5fd42 40 40 const double si = sas_sinx_x(q*A_s); 41 41 const double omsi = 1.0 - si; 42 const double pow_si = pow (si, num_pearls);42 const double pow_si = pown(si, num_pearls); 43 43 44 44 // form factor for num_pearls … … 81 81 radius_from_volume(double radius, double edge_sep, double thick_string, double fp_num_pearls) 82 82 { 83 const int num_pearls = (int) fp_num_pearls +0.5;84 83 const double vol_tot = form_volume(radius, edge_sep, thick_string, fp_num_pearls); 85 84 return cbrt(vol_tot/M_4PI_3);
Note: See TracChangeset
for help on using the changeset viewer.