|
18 | 18 | "metadata": {}, |
19 | 19 | "outputs": [], |
20 | 20 | "source": [ |
| 21 | + "import matplotlib.pyplot as plt\n", |
| 22 | + "import numpy as np\n", |
21 | 23 | "import scipp as sc\n", |
| 24 | + "import plopp as pp\n", |
22 | 25 | "import tof\n", |
23 | 26 | "\n", |
24 | 27 | "meter = sc.Unit('m')\n", |
|
412 | 415 | "id": "33", |
413 | 416 | "metadata": {}, |
414 | 417 | "source": [ |
415 | | - "## Loading from a JSON file\n", |
| 418 | + "## Inelastic sample\n", |
| 419 | + "\n", |
| 420 | + "Placing an `InelasticSample` in the instrument will change the energy of the incoming neutrons by a $\\Delta E$ defined in a function.\n", |
| 421 | + "That function takes in the incident energy and returns the final energy after the energy transfer took place.\n", |
| 422 | + "\n", |
| 423 | + "To give an equal chance for a random $\\Delta E$ between -0.2 and 0.2 meV, we can use Numpy's uniform random sampling:" |
| 424 | + ] |
| 425 | + }, |
| 426 | + { |
| 427 | + "cell_type": "code", |
| 428 | + "execution_count": null, |
| 429 | + "id": "34", |
| 430 | + "metadata": {}, |
| 431 | + "outputs": [], |
| 432 | + "source": [ |
| 433 | + "rng = np.random.default_rng(seed=83)\n", |
| 434 | + "\n", |
| 435 | + "\n", |
| 436 | + "def uniform_deltae(e_i):\n", |
| 437 | + " # Uniform sampling between -0.2 and 0.2 meV\n", |
| 438 | + " de = sc.array(\n", |
| 439 | + " dims=e_i.dims, values=rng.uniform(-0.2, 0.2, size=e_i.shape), unit='meV'\n", |
| 440 | + " )\n", |
| 441 | + " return e_i.to(unit='meV', copy=False) - de\n", |
| 442 | + "\n", |
| 443 | + "\n", |
| 444 | + "sample = tof.InelasticSample(distance=28.0 * meter, name=\"sample\", func=uniform_deltae)\n", |
| 445 | + "sample" |
| 446 | + ] |
| 447 | + }, |
| 448 | + { |
| 449 | + "cell_type": "markdown", |
| 450 | + "id": "35", |
| 451 | + "metadata": {}, |
| 452 | + "source": [ |
| 453 | + "We then make a single fast-rotating chopper with one small opening,\n", |
| 454 | + "to select a narrow wavelength range at every rotation.\n", |
| 455 | + "\n", |
| 456 | + "We also add a monitor before the sample, and a detector after the sample so we can follow the changes in energies/wavelengths." |
| 457 | + ] |
| 458 | + }, |
| 459 | + { |
| 460 | + "cell_type": "code", |
| 461 | + "execution_count": null, |
| 462 | + "id": "36", |
| 463 | + "metadata": {}, |
| 464 | + "outputs": [], |
| 465 | + "source": [ |
| 466 | + "choppers = [\n", |
| 467 | + " tof.Chopper(\n", |
| 468 | + " frequency=70.0 * Hz,\n", |
| 469 | + " open=sc.array(dims=['cutout'], values=[0.0], unit='deg'),\n", |
| 470 | + " close=sc.array(dims=['cutout'], values=[1.0], unit='deg'),\n", |
| 471 | + " phase=0.0 * deg,\n", |
| 472 | + " distance=20.0 * meter,\n", |
| 473 | + " name=\"fastchopper\",\n", |
| 474 | + " ),\n", |
| 475 | + "]\n", |
| 476 | + "\n", |
| 477 | + "detectors = [\n", |
| 478 | + " tof.Detector(distance=26.0 * meter, name='monitor'),\n", |
| 479 | + " tof.Detector(distance=32.0 * meter, name='detector'),\n", |
| 480 | + "]\n", |
| 481 | + "\n", |
| 482 | + "source = tof.Source(facility='ess', neutrons=5_000_000)\n", |
| 483 | + "\n", |
| 484 | + "model = tof.Model(source=source, components=choppers + detectors + [sample])\n", |
| 485 | + "res = model.run()\n", |
| 486 | + "\n", |
| 487 | + "\n", |
| 488 | + "fig, ax = plt.subplots(1, 2, figsize=(12, 4.5))\n", |
| 489 | + "\n", |
| 490 | + "dw = sc.scalar(0.1, unit='angstrom')\n", |
| 491 | + "pp.plot(\n", |
| 492 | + " {\n", |
| 493 | + " 'monitor': res['monitor'].data.hist(wavelength=dw),\n", |
| 494 | + " 'detector': res['detector'].data.hist(wavelength=dw),\n", |
| 495 | + " },\n", |
| 496 | + " title=\"With inelastic sample\",\n", |
| 497 | + " xmin=4,\n", |
| 498 | + " xmax=20,\n", |
| 499 | + " ymin=-20,\n", |
| 500 | + " ymax=400,\n", |
| 501 | + " ax=ax[1],\n", |
| 502 | + ")\n", |
| 503 | + "\n", |
| 504 | + "res.plot(visible_rays=10000, ax=ax[0])" |
| 505 | + ] |
| 506 | + }, |
| 507 | + { |
| 508 | + "cell_type": "markdown", |
| 509 | + "id": "37", |
| 510 | + "metadata": {}, |
| 511 | + "source": [ |
| 512 | + "### Non-uniform energy-transfer distributions" |
| 513 | + ] |
| 514 | + }, |
| 515 | + { |
| 516 | + "cell_type": "code", |
| 517 | + "execution_count": null, |
| 518 | + "id": "38", |
| 519 | + "metadata": {}, |
| 520 | + "outputs": [], |
| 521 | + "source": [ |
| 522 | + "# Sample 1: double-peak at min and max\n", |
| 523 | + "def double_peak(e_i):\n", |
| 524 | + " # Either -0.2 or 0.2 meV\n", |
| 525 | + " de = sc.array(\n", |
| 526 | + " dims=e_i.dims, values=rng.choice([-0.2, 0.2], size=e_i.shape), unit='meV'\n", |
| 527 | + " )\n", |
| 528 | + " return e_i.to(unit='meV', copy=False) - de\n", |
| 529 | + "\n", |
| 530 | + "\n", |
| 531 | + "sample1 = tof.InelasticSample(\n", |
| 532 | + " distance=28.0 * meter,\n", |
| 533 | + " name=\"sample\",\n", |
| 534 | + " func=double_peak,\n", |
| 535 | + ")\n", |
| 536 | + "\n", |
| 537 | + "\n", |
| 538 | + "# Sample 2: normal distribution\n", |
| 539 | + "def normal_deltae(e_i):\n", |
| 540 | + " de = sc.array(\n", |
| 541 | + " dims=e_i.dims, values=rng.normal(scale=0.05, size=e_i.shape), unit='meV'\n", |
| 542 | + " )\n", |
| 543 | + " return e_i.to(unit='meV', copy=False) - de\n", |
| 544 | + "\n", |
| 545 | + "\n", |
| 546 | + "sample2 = tof.InelasticSample(\n", |
| 547 | + " distance=28.0 * meter,\n", |
| 548 | + " name=\"sample\",\n", |
| 549 | + " func=normal_deltae,\n", |
| 550 | + ")" |
| 551 | + ] |
| 552 | + }, |
| 553 | + { |
| 554 | + "cell_type": "code", |
| 555 | + "execution_count": null, |
| 556 | + "id": "39", |
| 557 | + "metadata": {}, |
| 558 | + "outputs": [], |
| 559 | + "source": [ |
| 560 | + "model1 = tof.Model(source=source, components=choppers + detectors + [sample1])\n", |
| 561 | + "model2 = tof.Model(source=source, components=choppers + detectors + [sample2])\n", |
| 562 | + "\n", |
| 563 | + "res1 = model1.run()\n", |
| 564 | + "res2 = model2.run()\n", |
| 565 | + "\n", |
| 566 | + "fig, ax = plt.subplots(2, 2, figsize=(12, 9))\n", |
| 567 | + "\n", |
| 568 | + "res1.plot(visible_rays=10000, ax=ax[0, 0], title=\"Sample 1\")\n", |
| 569 | + "pp.plot(\n", |
| 570 | + " {\n", |
| 571 | + " 'monitor': res1['monitor'].data.hist(wavelength=dw),\n", |
| 572 | + " 'detector': res1['detector'].data.hist(wavelength=dw),\n", |
| 573 | + " },\n", |
| 574 | + " title=\"Sample 1\",\n", |
| 575 | + " xmin=4,\n", |
| 576 | + " xmax=20,\n", |
| 577 | + " ymin=-20,\n", |
| 578 | + " ymax=400,\n", |
| 579 | + " ax=ax[0, 1],\n", |
| 580 | + ")\n", |
| 581 | + "\n", |
| 582 | + "res2.plot(visible_rays=10000, ax=ax[1, 0], title=\"Sample 2\")\n", |
| 583 | + "_ = pp.plot(\n", |
| 584 | + " {\n", |
| 585 | + " 'monitor': res2['monitor'].data.hist(wavelength=dw),\n", |
| 586 | + " 'detector': res2['detector'].data.hist(wavelength=dw),\n", |
| 587 | + " },\n", |
| 588 | + " title=\"Sample 2\",\n", |
| 589 | + " xmin=4,\n", |
| 590 | + " xmax=20,\n", |
| 591 | + " ymin=-20,\n", |
| 592 | + " ymax=400,\n", |
| 593 | + " ax=ax[1, 1],\n", |
| 594 | + ")" |
| 595 | + ] |
| 596 | + }, |
| 597 | + { |
| 598 | + "cell_type": "markdown", |
| 599 | + "id": "40", |
| 600 | + "metadata": {}, |
| 601 | + "source": [ |
| 602 | + "## Loading components from a JSON file\n", |
416 | 603 | "\n", |
417 | 604 | "It is also possible to load components from a JSON file,\n", |
418 | 605 | "which can be very useful to quickly load a pre-configured instrument.\n", |
|
423 | 610 | { |
424 | 611 | "cell_type": "code", |
425 | 612 | "execution_count": null, |
426 | | - "id": "34", |
| 613 | + "id": "41", |
427 | 614 | "metadata": {}, |
428 | 615 | "outputs": [], |
429 | 616 | "source": [ |
430 | 617 | "import json\n", |
431 | 618 | "\n", |
432 | 619 | "params = {\n", |
433 | | - " \"source\": {\n", |
434 | | - " \"type\": \"source\",\n", |
435 | | - " \"facility\": \"ess\",\n", |
436 | | - " \"neutrons\": 1e6,\n", |
437 | | - " \"pulses\": 1\n", |
438 | | - " },\n", |
| 620 | + " \"source\": {\"type\": \"source\", \"facility\": \"ess\", \"neutrons\": 1e6, \"pulses\": 1},\n", |
439 | 621 | " \"chopper1\": {\n", |
440 | 622 | " \"type\": \"chopper\",\n", |
441 | 623 | " \"frequency\": {\"value\": 56.0, \"unit\": \"Hz\"},\n", |
|
470 | 652 | { |
471 | 653 | "cell_type": "code", |
472 | 654 | "execution_count": null, |
473 | | - "id": "35", |
| 655 | + "id": "42", |
474 | 656 | "metadata": {}, |
475 | 657 | "outputs": [], |
476 | 658 | "source": [ |
|
479 | 661 | }, |
480 | 662 | { |
481 | 663 | "cell_type": "markdown", |
482 | | - "id": "36", |
| 664 | + "id": "43", |
483 | 665 | "metadata": {}, |
484 | 666 | "source": [ |
485 | 667 | "We now use the `tof.Model.from_json()` method to load our instrument:" |
|
488 | 670 | { |
489 | 671 | "cell_type": "code", |
490 | 672 | "execution_count": null, |
491 | | - "id": "37", |
| 673 | + "id": "44", |
492 | 674 | "metadata": {}, |
493 | 675 | "outputs": [], |
494 | 676 | "source": [ |
|
498 | 680 | }, |
499 | 681 | { |
500 | 682 | "cell_type": "markdown", |
501 | | - "id": "38", |
| 683 | + "id": "45", |
502 | 684 | "metadata": {}, |
503 | 685 | "source": [ |
504 | 686 | "We can see that all components have been read in correctly.\n", |
|
508 | 690 | { |
509 | 691 | "cell_type": "code", |
510 | 692 | "execution_count": null, |
511 | | - "id": "39", |
| 693 | + "id": "46", |
512 | 694 | "metadata": {}, |
513 | 695 | "outputs": [], |
514 | 696 | "source": [ |
|
517 | 699 | }, |
518 | 700 | { |
519 | 701 | "cell_type": "markdown", |
520 | | - "id": "40", |
| 702 | + "id": "47", |
521 | 703 | "metadata": {}, |
522 | 704 | "source": [ |
523 | 705 | "### Modifying the source\n", |
|
531 | 713 | { |
532 | 714 | "cell_type": "code", |
533 | 715 | "execution_count": null, |
534 | | - "id": "41", |
| 716 | + "id": "48", |
535 | 717 | "metadata": {}, |
536 | 718 | "outputs": [], |
537 | 719 | "source": [ |
|
542 | 724 | }, |
543 | 725 | { |
544 | 726 | "cell_type": "markdown", |
545 | | - "id": "42", |
| 727 | + "id": "49", |
546 | 728 | "metadata": {}, |
547 | 729 | "source": [ |
548 | 730 | "## Saving to JSON\n", |
|
553 | 735 | { |
554 | 736 | "cell_type": "code", |
555 | 737 | "execution_count": null, |
556 | | - "id": "43", |
| 738 | + "id": "50", |
557 | 739 | "metadata": {}, |
558 | 740 | "outputs": [], |
559 | 741 | "source": [ |
|
0 commit comments