Changes in / [4057e06:8064d5e] in sasmodels
- Files:
-
- 6 added
- 6 deleted
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
explore/beta/sasfit_compare.py
r119073a r2a12351b 505 505 } 506 506 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'))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')) 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(' 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'))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')) 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
r275511c r7d97437 1 1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*-3 2 """ 4 3 Jitter Explorer … … 6 5 7 6 Application to explore orientation angle and angular dispersity. 8 9 From the command line::10 11 # Show docs12 python -m sasmodels.jitter --help13 14 # Guyou projection jitter, uniform over 20 degree theta and 10 in phi15 python -m sasmodels.jitter --projection=guyou --dist=uniform --jitter=20,10,016 17 From a jupyter cell::18 19 import ipyvolume as ipv20 from sasmodels import jitter21 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')47 7 """ 48 8 from __future__ import division, print_function … … 50 10 import argparse 51 11 12 try: # CRUFT: travis-ci does not support mpl_toolkits.mplot3d 13 import mpl_toolkits.mplot3d # Adds projection='3d' option to subplot 14 except ImportError: 15 pass 16 17 import matplotlib as mpl 18 import matplotlib.pyplot as plt 19 from matplotlib.widgets import Slider 52 20 import numpy as np 53 21 from numpy import pi, cos, sin, sqrt, exp, degrees, radians 54 22 55 def draw_beam(axes, view=(0, 0) , alpha=0.5, steps=25):23 def draw_beam(axes, view=(0, 0)): 56 24 """ 57 25 Draw the beam going from source at (0, 0, 1) to detector at (0, 0, -1) 58 26 """ 59 27 #axes.plot([0,0],[0,0],[1,-1]) 60 #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=alpha) 61 28 #axes.scatter([0]*100,[0]*100,np.linspace(1, -1, 100), alpha=0.8) 29 30 steps = 25 62 31 u = np.linspace(0, 2 * np.pi, steps) 63 v = np.linspace(-1, 1, 2)32 v = np.linspace(-1, 1, steps) 64 33 65 34 r = 0.02 … … 73 42 points = Rz(phi)*Ry(theta)*points 74 43 x, y, z = [v.reshape(shape) for v in points] 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 44 45 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='y', alpha=0.5) 88 46 89 47 def draw_ellipsoid(axes, size, view, jitter, steps=25, alpha=1): … … 97 55 x, y, z = transform_xyz(view, jitter, x, y, z) 98 56 99 axes.plot_surface(x, y, z, color='w', alpha=alpha)57 axes.plot_surface(x, y, z, rstride=4, cstride=4, color='w', alpha=alpha) 100 58 101 59 draw_labels(axes, view, jitter, [ … … 166 124 return atoms 167 125 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): 126 def draw_parallelepiped(axes, size, view, jitter, steps=None, alpha=1): 185 127 """Draw a parallelepiped.""" 186 128 a, b, c = size … … 200 142 201 143 x, y, z = transform_xyz(view, jitter, x, y, z) 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. 144 axes.plot_trisurf(x, y, triangles=tri, Z=z, color='w', alpha=alpha) 145 146 # Draw pink face on box. 206 147 # Since I can't control face color, instead draw a thin box situated just 207 148 # in front of the "c+" face. Use the c face so that rotations about psi 208 149 # rotate that face. 209 if 0: 210 color = (1, 0.6, 0.6) # pink 150 if 1: 211 151 x = a*np.array([+1, -1, +1, -1, +1, -1, +1, -1]) 212 152 y = b*np.array([+1, +1, -1, -1, +1, +1, -1, -1]) 213 153 z = c*np.array([+1, +1, +1, +1, -1, -1, -1, -1]) 214 154 x, y, z = transform_xyz(view, jitter, x, y, abs(z)+0.001) 215 axes.plot_trisurf(x, y, triangles=tri, Z=z, color= color, alpha=alpha)155 axes.plot_trisurf(x, y, triangles=tri, Z=z, color=[1, 0.6, 0.6], alpha=alpha) 216 156 217 157 draw_labels(axes, view, jitter, [ … … 224 164 ]) 225 165 226 def draw_sphere(axes, radius= 0.5, steps=25, center=(0,0,0), color='w', alpha=1.):166 def draw_sphere(axes, radius=10., steps=100): 227 167 """Draw a sphere""" 228 168 u = np.linspace(0, 2 * np.pi, steps) 229 169 v = np.linspace(0, np.pi, steps) 230 170 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): 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): 295 178 """ 296 179 Represent jitter as a set of shapes at different orientations. 297 180 """ 298 project, project_weight = get_projection(projection)299 300 181 # set max diagonal to 0.95 301 182 scale = 0.95/sqrt(sum(v**2 for v in size)) 302 183 size = tuple(scale*v for v in size) 303 184 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 ] 304 216 dtheta, dphi, dpsi = jitter 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) 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) 319 228 for v in 'xyz': 320 229 a, b, c = size 321 230 lim = np.sqrt(a**2 + b**2 + c**2) 322 231 getattr(axes, 'set_'+v+'lim')([-lim, lim]) 323 #getattr(axes, v+'axis').label.set_text(v)232 getattr(axes, v+'axis').label.set_text(v) 324 233 325 234 PROJECTIONS = [ … … 329 238 'azimuthal_equal_area', 330 239 ] 331 def get_projection(projection): 332 333 """ 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 334 246 jitter projections 335 247 <https://en.wikipedia.org/wiki/List_of_map_projections> … … 387 299 # TODO: try Kent distribution instead of a gaussian warped by projection 388 300 301 dist_x = np.linspace(-1, 1, n) 302 weights = np.ones_like(dist_x) 303 if dist == 'gaussian': 304 dist_x *= 3 305 weights = exp(-0.5*dist_x**2) 306 elif dist == 'rectangle': 307 # Note: uses sasmodels ridiculous definition of rectangle width 308 dist_x *= sqrt(3) 309 elif dist == 'uniform': 310 pass 311 else: 312 raise ValueError("expected dist to be gaussian, rectangle or uniform") 313 389 314 if projection == 'equirectangular': #define PROJECTION 1 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) 315 def _rotate(theta_i, phi_j): 316 return Rx(phi_j)*Ry(theta_i) 394 317 def _weight(theta_i, phi_j, w_i, w_j): 395 318 return w_i*w_j*abs(cos(radians(theta_i))) 396 319 elif projection == 'sinusoidal': #define PROJECTION 2 397 def _ project(theta_i, phi_j, psi):320 def _rotate(theta_i, phi_j): 398 321 latitude = theta_i 399 322 scale = cos(radians(latitude)) 400 323 longitude = phi_j/scale if abs(phi_j) < abs(scale)*180 else 0 401 324 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 402 return latitude, longitude, psi 403 #return Rx(longitude)*Ry(latitude) 404 def _project(theta_i, phi_j, w_i, w_j): 325 return Rx(longitude)*Ry(latitude) 326 def _weight(theta_i, phi_j, w_i, w_j): 405 327 latitude = theta_i 406 328 scale = cos(radians(latitude)) … … 408 330 return active*w_i*w_j 409 331 elif projection == 'guyou': #define PROJECTION 3 (eventually?) 410 def _ project(theta_i, phi_j, psi):332 def _rotate(theta_i, phi_j): 411 333 from .guyou import guyou_invert 412 334 #latitude, longitude = guyou_invert([theta_i], [phi_j]) 413 335 longitude, latitude = guyou_invert([phi_j], [theta_i]) 414 return latitude, longitude, psi 415 #return Rx(longitude[0])*Ry(latitude[0]) 336 return Rx(longitude[0])*Ry(latitude[0]) 416 337 def _weight(theta_i, phi_j, w_i, w_j): 417 338 return w_i*w_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): 339 elif projection == 'azimuthal_equidistance': # Note: Rz Ry, not Rx Ry 340 def _rotate(theta_i, phi_j): 421 341 latitude = sqrt(theta_i**2 + phi_j**2) 422 342 longitude = degrees(np.arctan2(phi_j, theta_i)) 423 343 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 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) 344 return Rz(longitude)*Ry(latitude) 428 345 def _weight(theta_i, phi_j, w_i, w_j): 429 346 # Weighting for each point comes from the integral: … … 459 376 return weight*w_i*w_j if latitude < 180 else 0 460 377 elif projection == 'azimuthal_equal_area': 461 # Note that calculates angles for Rz Ry rather than Rx Ry 462 def _project(theta_i, phi_j, psi): 378 def _rotate(theta_i, phi_j): 463 379 radius = min(1, sqrt(theta_i**2 + phi_j**2)/180) 464 380 latitude = 180-degrees(2*np.arccos(radius)) 465 381 longitude = degrees(np.arctan2(phi_j, theta_i)) 466 382 #print("(%+7.2f, %+7.2f) => (%+7.2f, %+7.2f)"%(theta_i, phi_j, latitude, longitude)) 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) 383 return Rz(longitude)*Ry(latitude) 471 384 def _weight(theta_i, phi_j, w_i, w_j): 472 385 latitude = sqrt(theta_i**2 + phi_j**2) … … 476 389 raise ValueError("unknown projection %r"%projection) 477 390 478 return _project, _weight479 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 Matrix485 Mike Day, Insomniac Games486 https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf487 Based on: Shoemakeâs "Euler Angle Conversion", Graphics Gems IV, pp. 222-229488 """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 which498 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 coords505 return Rz(dview[1])*Ry(dview[0])*z506 else:507 return Rx(dview[1])*Ry(dview[0])*z508 509 510 dist_x = np.linspace(-1, 1, n)511 weights = np.ones_like(dist_x)512 if dist == 'gaussian':513 dist_x *= 3514 weights = exp(-0.5*dist_x**2)515 elif dist == 'rectangle':516 # Note: uses sasmodels ridiculous definition of rectangle width517 dist_x *= sqrt(3)518 elif dist == 'uniform':519 pass520 else:521 raise ValueError("expected dist to be gaussian, rectangle or uniform")522 523 391 # mesh in theta, phi formed by rotating z 524 392 dtheta, dphi, dpsi = jitter 525 393 z = np.matrix([[0], [0], [radius]]) 526 points = np.hstack([_rotate(theta_i, phi_j , z)394 points = np.hstack([_rotate(theta_i, phi_j)*z 527 395 for theta_i in dtheta*dist_x 528 396 for phi_j in dphi*dist_x]) … … 602 470 Points are stored in a 3 x n numpy matrix, not a numpy array or tuple. 603 471 """ 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 472 dtheta, dphi, dpsi = jitter 473 points = Rx(dphi)*Ry(dtheta)*Rz(dpsi)*points 613 474 return points 614 475 … … 620 481 """ 621 482 theta, phi, psi = view 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 483 points = Rz(phi)*Ry(theta)*Rz(psi)*points 625 484 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 the634 computation. It works but it is not used because it doesn't solve635 any problems. The challenge of mapping theta/phi/psi to SO(3) does636 not disappear by calculating the transform differently.637 """638 theta, phi, psi = view639 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 rotating642 ## the unit vectors before applying the rotation.643 #q = Quaternion.from_angle_axis(theta, q.rot(x)) * q644 #q = Quaternion.from_angle_axis(phi, q.rot(y)) * q645 #q = Quaternion.from_angle_axis(psi, q.rot(z)) * q646 ## The above turns out to be equivalent to reversing647 ## 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-multiply652 #q = Quaternion.from_angle_axis(theta, x) * q653 #q = Quaternion.from_angle_axis(phi, y) * q654 #q = Quaternion.from_angle_axis(psi, z) * q655 #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_quaternion658 659 # Simple stand-alone quaternion class660 import numpy as np661 from copy import copy662 class Quaternion(object):663 def __init__(self, w, r):664 self.w = w665 self.r = np.asarray(r, dtype='d')666 @staticmethod667 def from_angle_axis(theta, r):668 theta = np.radians(theta)/2669 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).T680 use_transpose = (v.shape[-1] != 3)681 if use_transpose: v = v.T682 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.T685 return v.T686 def conj(self):687 return Quaternion(self.w, -self.r)688 def inv(self):689 return self.conj()/self.norm()**2690 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-14704 #test_qrot()705 #import sys; sys.exit()706 485 707 486 # translate between number of dimension of dispersity and the number of … … 777 556 vmin = vmax*10**-7 778 557 #vmin, vmax = clipped_range(Iqxy, portion=portion, mode='top') 779 #vmin, vmax = Iqxy.min(), Iqxy.max()780 558 #print("range",(vmin,vmax)) 781 559 #qx, qy = np.meshgrid(qx, qy) 782 560 if 0: 783 from matplotlib import cm784 561 level = np.asarray(255*(Iqxy - vmin)/(vmax - vmin), 'i') 785 562 level[level < 0] = 0 786 563 colors = plt.get_cmap()(level) 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) 564 axes.plot_surface(qx, qy, -1.1, rstride=1, cstride=1, facecolors=colors) 793 565 elif 1: 794 566 axes.contourf(qx/qx.max(), qy/qy.max(), Iqxy, zdir='z', offset=-1.1, … … 920 692 } 921 693 922 923 694 def run(model_name='parallelepiped', size=(10, 40, 100), 924 view=(0, 0, 0), jitter=(0, 0, 0),925 695 dist='gaussian', mesh=30, 926 696 projection='equirectangular'): … … 932 702 933 703 *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.938 704 939 705 *dist* is the type of dispersition: gaussian, rectangle, or uniform. … … 955 721 calculator, size = select_calculator(model_name, n=150, size=size) 956 722 draw_shape = DRAW_SHAPES.get(model_name, draw_parallelepiped) 957 #draw_shape = draw_fcc958 723 959 724 ## uncomment to set an independent the colour range for every view … … 961 726 calculator.limits = None 962 727 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 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 973 735 ## create the plot window 974 736 #plt.hold(True) … … 985 747 pass 986 748 987 988 749 # CRUFT: use axisbg instead of facecolor for matplotlib<2 989 750 facecolor_prop = 'facecolor' if mpl.__version__ > '2' else 'axisbg' … … 1001 762 axes_dphi = plt.axes([0.75, 0.1, 0.15, 0.04], **props) 1002 763 axes_dpsi = plt.axes([0.75, 0.05, 0.15, 0.04], **props) 1003 1004 764 # Note: using ridiculous definition of rectangle distribution, whose width 1005 765 # in sasmodels is sqrt(3) times the given width. Divide by sqrt(3) to keep 1006 766 # the maximum width to 90. 1007 767 dlimit = DIST_LIMITS[dist] 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) 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 1021 772 1022 773 ## callback to draw the new view … … 1026 777 # set small jitter as 0 if multiple pd dims 1027 778 dims = sum(v > 0 for v in jitter) 1028 limit = [0, 0.5, 5 , 5][dims]779 limit = [0, 0.5, 5][dims] 1029 780 jitter = [0 if v < limit else v for v in jitter] 1030 781 axes.cla() 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) 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)) 1044 785 draw_mesh(axes, view, jitter, dist=dist, n=mesh, projection=projection) 1045 786 draw_scattering(calculator, axes, view, jitter, dist=dist) 1046 1047 787 plt.gcf().canvas.draw() 1048 788 … … 1060 800 ## go interactive 1061 801 plt.show() 1062 1063 1064 def map_colors(z, kw):1065 from matplotlib import cm1066 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] = alpha1083 kw['color'] = color1084 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.Image1091 from matplotlib import cm1092 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 image1101 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'] = color1122 if alpha is not None:1123 color = kw['color']1124 #TODO: convert color to [r, g, b, a] if not already1125 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] = alpha1133 kw['color'] = color1134 1135 def ipv_set_transparency(kw, obj):1136 color = kw.get('color', None)1137 if (isinstance(color, np.ndarray)1138 and color.shape[-1] == 41139 and (color[..., 3] != 1.0).any()):1140 obj.material.transparent = True1141 obj.material.side = "FrontSide"1142 1143 def ipv_axes():1144 import ipyvolume as ipv1145 1146 class Plotter:1147 # transparency can be achieved by setting the following:1148 # mesh.color = [r, g, b, alpha]1149 # mesh.material.transparent = True1150 # mesh.material.side = "FrontSide"1151 # smooth(ish) rotation can be achieved by setting:1152 # slide.continuous_update = True1153 # figure.animation = 0.1154 # mesh.material.x = x1155 # mesh.material.y = y1156 # mesh.material.z = z1157 # maybe need to synchronize update of x/y/z to avoid shimmy when moving1158 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'] = facecolors1166 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 h1172 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 h1181 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 h1189 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 + offset1200 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 h1206 def text(self, *args, **kw):1207 pass1208 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 widgets1222 import ipyvolume as ipv1223 1224 axes = ipv_axes()1225 1226 def draw(view, jitter):1227 camera = ipv.gcf().camera1228 #print(ipv.gcf().__dict__.keys())1229 #print(dir(ipv.gcf()))1230 ipv.figure(animation=0.) # no animation when updating object mesh1231 1232 # set small jitter as 0 if multiple pd dims1233 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 globe1238 #draw_beam(axes, (0, 0))1239 #draw_sphere(axes)1240 #draw_person_on_sphere(axes, view)1241 1242 ## Move beam instead of shape1243 #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 scattering1247 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 = camera1260 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 namese1267 # Ξ Ï Ï ÎΞ ÎÏ ÎÏ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_ENGINE1320 PLOT_ENGINE = _ENGINES[name]1321 802 1322 803 def main(): … … 1330 811 parser.add_argument('-s', '--size', type=str, default='10,40,100', 1331 812 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')1336 813 parser.add_argument('-d', '--distribution', choices=DISTRIBUTIONS, 1337 814 default=DISTRIBUTIONS[0], … … 1342 819 help='oriented shape') 1343 820 opts = parser.parse_args() 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, 821 size = tuple(int(v) for v in opts.size.split(',')) 822 run(opts.shape, size=size, 1348 823 mesh=opts.mesh, dist=opts.distribution, 1349 824 projection=opts.projection) -
sasmodels/models/pearl_necklace.c
r9b5fd42 r99658f6 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 n(si, num_pearls);42 const double pow_si = pow(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; 83 84 const double vol_tot = form_volume(radius, edge_sep, thick_string, fp_num_pearls); 84 85 return cbrt(vol_tot/M_4PI_3);
Note: See TracChangeset
for help on using the changeset viewer.