diff --git a/.gitignore b/.gitignore index 485bbafb4bca310a818e31774d8cef93c041227b..a2624fcf7c55f2c2bd2d270b3f43d6c9bb2a5d78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target /.vscode /book/book -*.dot \ No newline at end of file +*.dot +*.svg diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..296268dde33964b9e5617a932dcd7a45324e9208 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,48 @@ +# This file is a template, and might need editing before it works on your project. +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Rust.gitlab-ci.yml + +# Official language image. Look for the different tagged releases at: +# https://hub.docker.com/r/library/rust/tags/ +image: "rust:latest" + +# Optional: Pick zero or more services to be used on all builds. +# Only needed when using a docker container to run your tests in. +# Check out: https://docs.gitlab.com/ee/ci/services/index.html +# services: +# - mysql:latest +# - redis:latest +# - postgres:latest + +# Optional: Install a C compiler, cmake and git into the container. +# You will often need this when you (or any of your dependencies) depends on C code. +# before_script: +# - apt-get update -yqq +# - apt-get install -yqq --no-install-recommends build-essential + +# Use cargo to test the project +test:cargo: + script: + - rustc --version && cargo --version # Print version info for debugging + - cargo test --workspace --verbose + +# Optional: Use a third party library to generate gitlab junit reports +# test:junit-report: +# script: +# Should be specified in Cargo.toml +# - cargo install junitify +# - cargo test -- --format=json -Z unstable-options --report-time | junitify --out $CI_PROJECT_DIR/tests/ +# artifacts: +# when: always +# reports: +# junit: $CI_PROJECT_DIR/tests/*.xml + +#deploy: +# stage: deploy +# script: echo "Define your deployment script!" +# environment: production diff --git a/Cargo.lock b/Cargo.lock index 4f2514891aa5059baa087a36e542d4f66e7789ac..e723310ae6fecf75a986ac7315e8057b75ac5a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,24 +2,392 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "font-kit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" +dependencies = [ + "bitflags", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -30,6 +398,64 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + [[package]] name = "matrixmultiply" version = "0.3.7" @@ -40,6 +466,22 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + [[package]] name = "ndarray" version = "0.15.6" @@ -54,14 +496,27 @@ dependencies = [ ] [[package]] -name = "ndarray-interp" -version = "0.1.1" +name = "ndarray-stats" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16eb7477aebbc0c82bcc496d2d727e76875f800060a7eeac125d1f884a129750" +checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400" dependencies = [ + "indexmap", + "itertools", "ndarray", + "noisy_float", + "num-integer", + "num-traits", + "rand", +] + +[[package]] +name = "noisy_float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978fe6e6ebc0bf53de533cd456ca2d9de13de13856eda1518a285d7705a213af" +dependencies = [ "num-traits", - "thiserror", ] [[package]] @@ -83,25 +538,73 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "opossum" version = "0.1.0" dependencies = [ + "csv", "ndarray", - "ndarray-interp", + "ndarray-stats", "petgraph", + "plotters", "uom", ] +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pest" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +dependencies = [ + "thiserror", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.6.3" @@ -114,52 +617,221 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-bitmap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cebbe1f70205299abc69e8b295035bb52a6a70ee35474ad10011f0a4efb8543" +dependencies = [ + "gif", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" -version = "1.0.171" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "syn" -version = "2.0.25" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -168,42 +840,284 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "ttf-parser" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "uom" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f50eddd69f656ee545f7663ea5fefb7c789bc1a0d11124e049715f563a16a4" +checksum = "8362194c7a9845a7a7f3562173d6e1da3f24f7132018cb78fe77a5b4474187b2" dependencies = [ "num-traits", "typenum", ] + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index b3002e4bf5ddcc587621d839c34cf19db71e114a..635c1137805fc0ebab2556e76ed49288df5036e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,6 @@ uom = "0" # for spectrum ndarray="0" -ndarray-interp="0" \ No newline at end of file +ndarray-stats="0" +csv="1" +plotters="0" diff --git a/NE03B.csv b/NE03B.csv new file mode 100644 index 0000000000000000000000000000000000000000..f29e1ebdbbbd29e82bdd6a373b28e938d84a669d --- /dev/null +++ b/NE03B.csv @@ -0,0 +1,3202 @@ +200;1.433E-02 +201;1.408E-02 +202;1.414E-02 +203;1.395E-02 +204;1.386E-02 +205;1.384E-02 +206;1.365E-02 +207;1.347E-02 +208;1.351E-02 +209;1.333E-02 +210;1.313E-02 +211;1.300E-02 +212;1.296E-02 +213;1.292E-02 +214;1.293E-02 +215;1.264E-02 +216;1.253E-02 +217;1.241E-02 +218;1.236E-02 +219;1.228E-02 +220;1.217E-02 +221;1.203E-02 +222;1.198E-02 +223;1.190E-02 +224;1.184E-02 +225;1.176E-02 +226;1.168E-02 +227;1.162E-02 +228;1.150E-02 +229;1.138E-02 +230;1.129E-02 +231;1.134E-02 +232;1.136E-02 +233;1.119E-02 +234;1.112E-02 +235;1.107E-02 +236;1.105E-02 +237;1.097E-02 +238;1.091E-02 +239;1.092E-02 +240;1.075E-02 +241;1.071E-02 +242;1.060E-02 +243;1.053E-02 +244;1.056E-02 +245;1.046E-02 +246;1.041E-02 +247;1.026E-02 +248;1.028E-02 +249;1.021E-02 +250;1.027E-02 +251;1.015E-02 +252;1.011E-02 +253;1.001E-02 +254;1.002E-02 +255;1.002E-02 +256;1.001E-02 +257;9.875E-03 +258;9.907E-03 +259;9.758E-03 +260;9.743E-03 +261;9.764E-03 +262;9.866E-03 +263;9.717E-03 +264;9.686E-03 +265;9.617E-03 +266;9.597E-03 +267;9.608E-03 +268;9.595E-03 +269;9.479E-03 +270;9.447E-03 +271;9.406E-03 +272;9.438E-03 +273;9.352E-03 +274;9.482E-03 +275;9.330E-03 +276;9.281E-03 +277;9.251E-03 +278;9.210E-03 +279;9.207E-03 +280;9.218E-03 +281;9.089E-03 +282;9.097E-03 +283;9.031E-03 +284;9.088E-03 +285;9.075E-03 +286;9.074E-03 +287;9.000E-03 +288;8.956E-03 +289;8.907E-03 +290;8.945E-03 +291;8.919E-03 +292;8.930E-03 +293;8.763E-03 +294;8.749E-03 +295;8.695E-03 +296;8.703E-03 +297;8.711E-03 +298;8.806E-03 +299;8.720E-03 +300;8.641E-03 +301;8.650E-03 +302;8.660E-03 +303;8.573E-03 +304;8.574E-03 +305;8.525E-03 +306;8.533E-03 +307;8.515E-03 +308;8.463E-03 +309;8.590E-03 +310;8.608E-03 +311;8.494E-03 +312;8.378E-03 +313;8.394E-03 +314;8.422E-03 +315;8.493E-03 +316;8.687E-03 +317;8.988E-03 +318;9.707E-03 +319;1.033E-02 +320;1.308E-02 +321;1.807E-02 +322;2.646E-02 +323;3.949E-02 +324;6.031E-02 +325;9.204E-02 +326;1.379E-01 +327;2.064E-01 +328;2.996E-01 +329;4.238E-01 +330;5.878E-01 +331;7.960E-01 +332;1.056E+00 +333;1.367E+00 +334;1.736E+00 +335;2.160E+00 +336;2.658E+00 +337;3.246E+00 +338;3.903E+00 +339;4.662E+00 +340;5.470E+00 +341;6.323E+00 +342;7.248E+00 +343;8.200E+00 +344;9.247E+00 +345;1.034E+01 +346;1.150E+01 +347;1.259E+01 +348;1.383E+01 +349;1.506E+01 +350;1.636E+01 +351;1.770E+01 +352;1.898E+01 +353;2.007E+01 +354;2.130E+01 +355;2.254E+01 +356;2.381E+01 +357;2.509E+01 +358;2.632E+01 +359;2.729E+01 +360;2.841E+01 +361;2.947E+01 +362;3.059E+01 +363;3.157E+01 +364;3.253E+01 +365;3.316E+01 +366;3.389E+01 +367;3.438E+01 +368;3.485E+01 +369;3.521E+01 +370;3.520E+01 +371;3.463E+01 +372;3.398E+01 +373;3.310E+01 +374;3.211E+01 +375;3.126E+01 +376;3.048E+01 +377;2.987E+01 +378;3.006E+01 +379;3.063E+01 +380;3.158E+01 +381;3.271E+01 +382;3.415E+01 +383;3.571E+01 +384;3.746E+01 +385;3.902E+01 +386;4.052E+01 +387;4.168E+01 +388;4.306E+01 +389;4.416E+01 +390;4.536E+01 +391;4.627E+01 +392;4.703E+01 +393;4.740E+01 +394;4.807E+01 +395;4.858E+01 +396;4.925E+01 +397;4.955E+01 +398;4.992E+01 +399;4.983E+01 +400;4.999E+01 +401;5.005E+01 +402;5.028E+01 +403;5.028E+01 +404;5.024E+01 +405;4.982E+01 +406;4.973E+01 +407;4.961E+01 +408;4.977E+01 +409;4.952E+01 +410;4.935E+01 +411;4.902E+01 +412;4.902E+01 +413;4.895E+01 +414;4.907E+01 +415;4.908E+01 +416;4.909E+01 +417;4.890E+01 +418;4.901E+01 +419;4.917E+01 +420;4.936E+01 +421;4.967E+01 +422;4.970E+01 +423;4.959E+01 +424;4.976E+01 +425;4.993E+01 +426;5.015E+01 +427;5.046E+01 +428;5.046E+01 +429;5.033E+01 +430;5.036E+01 +431;5.044E+01 +432;5.051E+01 +433;5.060E+01 +434;5.041E+01 +435;5.016E+01 +436;5.003E+01 +437;4.996E+01 +438;5.001E+01 +439;5.014E+01 +440;5.003E+01 +441;4.980E+01 +442;4.982E+01 +443;5.000E+01 +444;5.021E+01 +445;5.054E+01 +446;5.063E+01 +447;5.061E+01 +448;5.082E+01 +449;5.106E+01 +450;5.138E+01 +451;5.185E+01 +452;5.193E+01 +453;5.188E+01 +454;5.203E+01 +455;5.215E+01 +456;5.244E+01 +457;5.279E+01 +458;5.280E+01 +459;5.266E+01 +460;5.264E+01 +461;5.272E+01 +462;5.288E+01 +463;5.305E+01 +464;5.291E+01 +465;5.266E+01 +466;5.265E+01 +467;5.260E+01 +468;5.267E+01 +469;5.279E+01 +470;5.251E+01 +471;5.222E+01 +472;5.217E+01 +473;5.215E+01 +474;5.221E+01 +475;5.215E+01 +476;5.201E+01 +477;5.176E+01 +478;5.163E+01 +479;5.154E+01 +480;5.163E+01 +481;5.164E+01 +482;5.144E+01 +483;5.112E+01 +484;5.099E+01 +485;5.090E+01 +486;5.098E+01 +487;5.107E+01 +488;5.083E+01 +489;5.054E+01 +490;5.046E+01 +491;5.041E+01 +492;5.050E+01 +493;5.059E+01 +494;5.036E+01 +495;5.020E+01 +496;5.017E+01 +497;5.018E+01 +498;5.032E+01 +499;5.031E+01 +500;5.010E+01 +501;4.983E+01 +502;4.981E+01 +503;4.984E+01 +504;4.996E+01 +505;5.010E+01 +506;4.992E+01 +507;4.974E+01 +508;4.973E+01 +509;4.970E+01 +510;4.987E+01 +511;5.004E+01 +512;4.987E+01 +513;4.975E+01 +514;4.979E+01 +515;4.983E+01 +516;5.005E+01 +517;5.026E+01 +518;5.011E+01 +519;4.999E+01 +520;5.003E+01 +521;5.013E+01 +522;5.035E+01 +523;5.052E+01 +524;5.042E+01 +525;5.032E+01 +526;5.040E+01 +527;5.050E+01 +528;5.076E+01 +529;5.097E+01 +530;5.083E+01 +531;5.077E+01 +532;5.085E+01 +533;5.094E+01 +534;5.118E+01 +535;5.137E+01 +536;5.129E+01 +537;5.121E+01 +538;5.128E+01 +539;5.136E+01 +540;5.160E+01 +541;5.174E+01 +542;5.154E+01 +543;5.141E+01 +544;5.146E+01 +545;5.162E+01 +546;5.178E+01 +547;5.183E+01 +548;5.157E+01 +549;5.140E+01 +550;5.141E+01 +551;5.152E+01 +552;5.164E+01 +553;5.146E+01 +554;5.131E+01 +555;5.122E+01 +556;5.118E+01 +557;5.128E+01 +558;5.132E+01 +559;5.123E+01 +560;5.094E+01 +561;5.091E+01 +562;5.069E+01 +563;5.057E+01 +564;5.043E+01 +565;5.049E+01 +566;5.045E+01 +567;5.017E+01 +568;4.990E+01 +569;4.969E+01 +570;4.953E+01 +571;4.948E+01 +572;4.955E+01 +573;4.933E+01 +574;4.908E+01 +575;4.887E+01 +576;4.870E+01 +577;4.868E+01 +578;4.868E+01 +579;4.846E+01 +580;4.815E+01 +581;4.798E+01 +582;4.789E+01 +583;4.794E+01 +584;4.791E+01 +585;4.772E+01 +586;4.754E+01 +587;4.736E+01 +588;4.728E+01 +589;4.734E+01 +590;4.739E+01 +591;4.725E+01 +592;4.712E+01 +593;4.710E+01 +594;4.701E+01 +595;4.718E+01 +596;4.724E+01 +597;4.716E+01 +598;4.704E+01 +599;4.710E+01 +600;4.709E+01 +601;4.714E+01 +602;4.727E+01 +603;4.721E+01 +604;4.714E+01 +605;4.718E+01 +606;4.717E+01 +607;4.734E+01 +608;4.744E+01 +609;4.742E+01 +610;4.730E+01 +611;4.746E+01 +612;4.739E+01 +613;4.755E+01 +614;4.761E+01 +615;4.754E+01 +616;4.745E+01 +617;4.744E+01 +618;4.748E+01 +619;4.762E+01 +620;4.772E+01 +621;4.757E+01 +622;4.748E+01 +623;4.758E+01 +624;4.750E+01 +625;4.772E+01 +626;4.772E+01 +627;4.744E+01 +628;4.733E+01 +629;4.736E+01 +630;4.728E+01 +631;4.731E+01 +632;4.727E+01 +633;4.705E+01 +634;4.693E+01 +635;4.695E+01 +636;4.690E+01 +637;4.690E+01 +638;4.694E+01 +639;4.666E+01 +640;4.658E+01 +641;4.653E+01 +642;4.653E+01 +643;4.654E+01 +644;4.648E+01 +645;4.630E+01 +646;4.625E+01 +647;4.628E+01 +648;4.623E+01 +649;4.631E+01 +650;4.631E+01 +651;4.608E+01 +652;4.608E+01 +653;4.603E+01 +654;4.609E+01 +655;4.628E+01 +656;4.643E+01 +657;4.606E+01 +658;4.610E+01 +659;4.628E+01 +660;4.630E+01 +661;4.656E+01 +662;4.649E+01 +663;4.644E+01 +664;4.648E+01 +665;4.666E+01 +666;4.678E+01 +667;4.700E+01 +668;4.724E+01 +669;4.717E+01 +670;4.727E+01 +671;4.741E+01 +672;4.778E+01 +673;4.803E+01 +674;4.809E+01 +675;4.815E+01 +676;4.822E+01 +677;4.848E+01 +678;4.871E+01 +679;4.915E+01 +680;4.933E+01 +681;4.933E+01 +682;4.961E+01 +683;5.000E+01 +684;5.021E+01 +685;5.063E+01 +686;5.060E+01 +687;5.067E+01 +688;5.079E+01 +689;5.133E+01 +690;5.132E+01 +691;5.149E+01 +692;5.178E+01 +693;5.199E+01 +694;5.192E+01 +695;5.216E+01 +696;5.219E+01 +697;5.255E+01 +698;5.290E+01 +699;5.268E+01 +700;5.265E+01 +701;5.291E+01 +702;5.294E+01 +703;5.318E+01 +704;5.338E+01 +705;5.333E+01 +706;5.322E+01 +707;5.326E+01 +708;5.318E+01 +709;5.352E+01 +710;5.379E+01 +711;5.337E+01 +712;5.352E+01 +713;5.353E+01 +714;5.344E+01 +715;5.372E+01 +716;5.378E+01 +717;5.357E+01 +718;5.345E+01 +719;5.352E+01 +720;5.338E+01 +721;5.360E+01 +722;5.361E+01 +723;5.337E+01 +724;5.323E+01 +725;5.319E+01 +726;5.322E+01 +727;5.329E+01 +728;5.336E+01 +729;5.306E+01 +730;5.296E+01 +731;5.288E+01 +732;5.276E+01 +733;5.286E+01 +734;5.305E+01 +735;5.267E+01 +736;5.259E+01 +737;5.258E+01 +738;5.241E+01 +739;5.257E+01 +740;5.270E+01 +741;5.226E+01 +742;5.217E+01 +743;5.214E+01 +744;5.206E+01 +745;5.201E+01 +746;5.223E+01 +747;5.189E+01 +748;5.162E+01 +749;5.157E+01 +750;5.251E+01 +751;5.247E+01 +752;5.243E+01 +753;5.240E+01 +754;5.237E+01 +755;5.234E+01 +756;5.232E+01 +757;5.228E+01 +758;5.224E+01 +759;5.220E+01 +760;5.217E+01 +761;5.213E+01 +762;5.210E+01 +763;5.205E+01 +764;5.200E+01 +765;5.196E+01 +766;5.193E+01 +767;5.188E+01 +768;5.184E+01 +769;5.180E+01 +770;5.174E+01 +771;5.170E+01 +772;5.166E+01 +773;5.162E+01 +774;5.153E+01 +775;5.145E+01 +776;5.136E+01 +777;5.127E+01 +778;5.118E+01 +779;5.109E+01 +780;5.100E+01 +781;5.091E+01 +782;5.082E+01 +783;5.073E+01 +784;5.064E+01 +785;5.054E+01 +786;5.045E+01 +787;5.035E+01 +788;5.026E+01 +789;5.016E+01 +790;5.007E+01 +791;4.997E+01 +792;4.988E+01 +793;4.978E+01 +794;4.968E+01 +795;4.959E+01 +796;4.949E+01 +797;4.940E+01 +798;4.930E+01 +799;4.921E+01 +800;4.911E+01 +801;4.902E+01 +802;4.892E+01 +803;4.883E+01 +804;4.873E+01 +805;4.864E+01 +806;4.854E+01 +807;4.845E+01 +808;4.835E+01 +809;4.826E+01 +810;4.817E+01 +811;4.808E+01 +812;4.798E+01 +813;4.789E+01 +814;4.780E+01 +815;4.770E+01 +816;4.761E+01 +817;4.752E+01 +818;4.743E+01 +819;4.733E+01 +820;4.724E+01 +821;4.715E+01 +822;4.705E+01 +823;4.696E+01 +824;4.687E+01 +825;4.678E+01 +826;4.669E+01 +827;4.659E+01 +828;4.650E+01 +829;4.641E+01 +830;4.632E+01 +831;4.623E+01 +832;4.614E+01 +833;4.605E+01 +834;4.595E+01 +835;4.587E+01 +836;4.578E+01 +837;4.569E+01 +838;4.560E+01 +839;4.551E+01 +840;4.543E+01 +841;4.534E+01 +842;4.525E+01 +843;4.517E+01 +844;4.508E+01 +845;4.499E+01 +846;4.491E+01 +847;4.482E+01 +848;4.474E+01 +849;4.466E+01 +850;4.458E+01 +851;4.449E+01 +852;4.441E+01 +853;4.433E+01 +854;4.425E+01 +855;4.417E+01 +856;4.409E+01 +857;4.401E+01 +858;4.393E+01 +859;4.385E+01 +860;4.377E+01 +861;4.369E+01 +862;4.361E+01 +863;4.353E+01 +864;4.345E+01 +865;4.337E+01 +866;4.329E+01 +867;4.320E+01 +868;4.312E+01 +869;4.303E+01 +870;4.294E+01 +871;4.285E+01 +872;4.276E+01 +873;4.266E+01 +874;4.256E+01 +875;4.246E+01 +876;4.236E+01 +877;4.226E+01 +878;4.215E+01 +879;4.205E+01 +880;4.194E+01 +881;4.184E+01 +882;4.172E+01 +883;4.161E+01 +884;4.149E+01 +885;4.138E+01 +886;4.126E+01 +887;4.114E+01 +888;4.102E+01 +889;4.090E+01 +890;4.079E+01 +891;4.067E+01 +892;4.056E+01 +893;4.045E+01 +894;4.034E+01 +895;4.024E+01 +896;4.014E+01 +897;4.004E+01 +898;3.995E+01 +899;3.987E+01 +900;3.978E+01 +901;3.968E+01 +902;3.959E+01 +903;3.951E+01 +904;3.942E+01 +905;3.934E+01 +906;3.926E+01 +907;3.919E+01 +908;3.912E+01 +909;3.905E+01 +910;3.898E+01 +911;3.891E+01 +912;3.885E+01 +913;3.878E+01 +914;3.872E+01 +915;3.865E+01 +916;3.859E+01 +917;3.853E+01 +918;3.847E+01 +919;3.840E+01 +920;3.834E+01 +921;3.827E+01 +922;3.819E+01 +923;3.812E+01 +924;3.805E+01 +925;3.799E+01 +926;3.793E+01 +927;3.786E+01 +928;3.780E+01 +929;3.774E+01 +930;3.768E+01 +931;3.761E+01 +932;3.755E+01 +933;3.750E+01 +934;3.744E+01 +935;3.737E+01 +936;3.730E+01 +937;3.724E+01 +938;3.718E+01 +939;3.712E+01 +940;3.706E+01 +941;3.700E+01 +942;3.694E+01 +943;3.687E+01 +944;3.682E+01 +945;3.677E+01 +946;3.672E+01 +947;3.666E+01 +948;3.660E+01 +949;3.655E+01 +950;3.650E+01 +951;3.645E+01 +952;3.639E+01 +953;3.633E+01 +954;3.628E+01 +955;3.622E+01 +956;3.616E+01 +957;3.610E+01 +958;3.605E+01 +959;3.599E+01 +960;3.594E+01 +961;3.588E+01 +962;3.583E+01 +963;3.577E+01 +964;3.572E+01 +965;3.567E+01 +966;3.562E+01 +967;3.557E+01 +968;3.551E+01 +969;3.546E+01 +970;3.541E+01 +971;3.537E+01 +972;3.532E+01 +973;3.526E+01 +974;3.520E+01 +975;3.515E+01 +976;3.510E+01 +977;3.505E+01 +978;3.500E+01 +979;3.495E+01 +980;3.491E+01 +981;3.486E+01 +982;3.482E+01 +983;3.477E+01 +984;3.472E+01 +985;3.468E+01 +986;3.463E+01 +987;3.459E+01 +988;3.454E+01 +989;3.449E+01 +990;3.444E+01 +991;3.439E+01 +992;3.435E+01 +993;3.430E+01 +994;3.425E+01 +995;3.421E+01 +996;3.417E+01 +997;3.413E+01 +998;3.409E+01 +999;3.405E+01 +1000;3.400E+01 +1001;3.396E+01 +1002;3.392E+01 +1003;3.388E+01 +1004;3.384E+01 +1005;3.380E+01 +1006;3.376E+01 +1007;3.372E+01 +1008;3.368E+01 +1009;3.365E+01 +1010;3.361E+01 +1011;3.357E+01 +1012;3.354E+01 +1013;3.351E+01 +1014;3.348E+01 +1015;3.345E+01 +1016;3.342E+01 +1017;3.339E+01 +1018;3.335E+01 +1019;3.332E+01 +1020;3.328E+01 +1021;3.325E+01 +1022;3.322E+01 +1023;3.319E+01 +1024;3.316E+01 +1025;3.312E+01 +1026;3.309E+01 +1027;3.305E+01 +1028;3.302E+01 +1029;3.298E+01 +1030;3.295E+01 +1031;3.292E+01 +1032;3.289E+01 +1033;3.285E+01 +1034;3.282E+01 +1035;3.279E+01 +1036;3.275E+01 +1037;3.271E+01 +1038;3.267E+01 +1039;3.264E+01 +1040;3.260E+01 +1041;3.257E+01 +1042;3.254E+01 +1043;3.250E+01 +1044;3.246E+01 +1045;3.243E+01 +1046;3.239E+01 +1047;3.237E+01 +1048;3.234E+01 +1049;3.231E+01 +1050;3.229E+01 +1051;3.226E+01 +1052;3.224E+01 +1053;3.222E+01 +1054;3.219E+01 +1055;3.216E+01 +1056;3.213E+01 +1057;3.211E+01 +1058;3.208E+01 +1059;3.206E+01 +1060;3.203E+01 +1061;3.200E+01 +1062;3.197E+01 +1063;3.195E+01 +1064;3.193E+01 +1065;3.191E+01 +1066;3.189E+01 +1067;3.188E+01 +1068;3.185E+01 +1069;3.183E+01 +1070;3.181E+01 +1071;3.179E+01 +1072;3.176E+01 +1073;3.173E+01 +1074;3.170E+01 +1075;3.168E+01 +1076;3.165E+01 +1077;3.163E+01 +1078;3.161E+01 +1079;3.160E+01 +1080;3.159E+01 +1081;3.157E+01 +1082;3.156E+01 +1083;3.156E+01 +1084;3.155E+01 +1085;3.155E+01 +1086;3.153E+01 +1087;3.151E+01 +1088;3.149E+01 +1089;3.148E+01 +1090;3.146E+01 +1091;3.145E+01 +1092;3.144E+01 +1093;3.143E+01 +1094;3.143E+01 +1095;3.142E+01 +1096;3.142E+01 +1097;3.141E+01 +1098;3.140E+01 +1099;3.139E+01 +1100;3.139E+01 +1101;3.138E+01 +1102;3.137E+01 +1103;3.136E+01 +1104;3.134E+01 +1105;3.132E+01 +1106;3.131E+01 +1107;3.129E+01 +1108;3.128E+01 +1109;3.127E+01 +1110;3.126E+01 +1111;3.124E+01 +1112;3.123E+01 +1113;3.121E+01 +1114;3.119E+01 +1115;3.118E+01 +1116;3.117E+01 +1117;3.115E+01 +1118;3.114E+01 +1119;3.113E+01 +1120;3.112E+01 +1121;3.111E+01 +1122;3.111E+01 +1123;3.110E+01 +1124;3.110E+01 +1125;3.110E+01 +1126;3.110E+01 +1127;3.110E+01 +1128;3.110E+01 +1129;3.110E+01 +1130;3.110E+01 +1131;3.111E+01 +1132;3.111E+01 +1133;3.111E+01 +1134;3.112E+01 +1135;3.113E+01 +1136;3.114E+01 +1137;3.116E+01 +1138;3.117E+01 +1139;3.117E+01 +1140;3.118E+01 +1141;3.118E+01 +1142;3.118E+01 +1143;3.118E+01 +1144;3.118E+01 +1145;3.118E+01 +1146;3.117E+01 +1147;3.115E+01 +1148;3.115E+01 +1149;3.114E+01 +1150;3.114E+01 +1151;3.114E+01 +1152;3.113E+01 +1153;3.113E+01 +1154;3.112E+01 +1155;3.112E+01 +1156;3.112E+01 +1157;3.113E+01 +1158;3.113E+01 +1159;3.112E+01 +1160;3.112E+01 +1161;3.111E+01 +1162;3.111E+01 +1163;3.111E+01 +1164;3.111E+01 +1165;3.111E+01 +1166;3.112E+01 +1167;3.114E+01 +1168;3.116E+01 +1169;3.118E+01 +1170;3.121E+01 +1171;3.123E+01 +1172;3.125E+01 +1173;3.126E+01 +1174;3.128E+01 +1175;3.129E+01 +1176;3.130E+01 +1177;3.131E+01 +1178;3.132E+01 +1179;3.132E+01 +1180;3.133E+01 +1181;3.134E+01 +1182;3.135E+01 +1183;3.138E+01 +1184;3.141E+01 +1185;3.144E+01 +1186;3.147E+01 +1187;3.150E+01 +1188;3.153E+01 +1189;3.156E+01 +1190;3.160E+01 +1191;3.164E+01 +1192;3.167E+01 +1193;3.171E+01 +1194;3.174E+01 +1195;3.177E+01 +1196;3.181E+01 +1197;3.184E+01 +1198;3.188E+01 +1199;3.193E+01 +1200;3.198E+01 +1201;3.203E+01 +1202;3.208E+01 +1203;3.213E+01 +1204;3.218E+01 +1205;3.222E+01 +1206;3.226E+01 +1207;3.230E+01 +1208;3.234E+01 +1209;3.237E+01 +1210;3.240E+01 +1211;3.242E+01 +1212;3.244E+01 +1213;3.244E+01 +1214;3.244E+01 +1215;3.244E+01 +1216;3.244E+01 +1217;3.244E+01 +1218;3.245E+01 +1219;3.246E+01 +1220;3.248E+01 +1221;3.249E+01 +1222;3.250E+01 +1223;3.250E+01 +1224;3.250E+01 +1225;3.250E+01 +1226;3.250E+01 +1227;3.251E+01 +1228;3.251E+01 +1229;3.250E+01 +1230;3.249E+01 +1231;3.248E+01 +1232;3.246E+01 +1233;3.245E+01 +1234;3.244E+01 +1235;3.244E+01 +1236;3.245E+01 +1237;3.246E+01 +1238;3.247E+01 +1239;3.248E+01 +1240;3.250E+01 +1241;3.251E+01 +1242;3.253E+01 +1243;3.254E+01 +1244;3.256E+01 +1245;3.258E+01 +1246;3.260E+01 +1247;3.262E+01 +1248;3.264E+01 +1249;3.266E+01 +1250;3.269E+01 +1251;3.272E+01 +1252;3.276E+01 +1253;3.281E+01 +1254;3.285E+01 +1255;3.290E+01 +1256;3.295E+01 +1257;3.301E+01 +1258;3.306E+01 +1259;3.310E+01 +1260;3.314E+01 +1261;3.319E+01 +1262;3.324E+01 +1263;3.329E+01 +1264;3.333E+01 +1265;3.336E+01 +1266;3.339E+01 +1267;3.341E+01 +1268;3.343E+01 +1269;3.345E+01 +1270;3.347E+01 +1271;3.349E+01 +1272;3.351E+01 +1273;3.354E+01 +1274;3.355E+01 +1275;3.357E+01 +1276;3.359E+01 +1277;3.361E+01 +1278;3.364E+01 +1279;3.367E+01 +1280;3.370E+01 +1281;3.373E+01 +1282;3.377E+01 +1283;3.380E+01 +1284;3.383E+01 +1285;3.386E+01 +1286;3.389E+01 +1287;3.392E+01 +1288;3.398E+01 +1289;3.404E+01 +1290;3.410E+01 +1291;3.417E+01 +1292;3.424E+01 +1293;3.431E+01 +1294;3.438E+01 +1295;3.445E+01 +1296;3.452E+01 +1297;3.459E+01 +1298;3.466E+01 +1299;3.473E+01 +1300;3.480E+01 +1301;3.486E+01 +1302;3.493E+01 +1303;3.498E+01 +1304;3.504E+01 +1305;3.510E+01 +1306;3.516E+01 +1307;3.522E+01 +1308;3.529E+01 +1309;3.536E+01 +1310;3.542E+01 +1311;3.547E+01 +1312;3.552E+01 +1313;3.556E+01 +1314;3.561E+01 +1315;3.565E+01 +1316;3.569E+01 +1317;3.574E+01 +1318;3.579E+01 +1319;3.583E+01 +1320;3.588E+01 +1321;3.592E+01 +1322;3.597E+01 +1323;3.601E+01 +1324;3.605E+01 +1325;3.609E+01 +1326;3.613E+01 +1327;3.618E+01 +1328;3.623E+01 +1329;3.627E+01 +1330;3.631E+01 +1331;3.634E+01 +1332;3.636E+01 +1333;3.638E+01 +1334;3.641E+01 +1335;3.643E+01 +1336;3.646E+01 +1337;3.649E+01 +1338;3.651E+01 +1339;3.653E+01 +1340;3.655E+01 +1341;3.658E+01 +1342;3.661E+01 +1343;3.665E+01 +1344;3.670E+01 +1345;3.674E+01 +1346;3.679E+01 +1347;3.684E+01 +1348;3.689E+01 +1349;3.695E+01 +1350;3.701E+01 +1351;3.707E+01 +1352;3.713E+01 +1353;3.720E+01 +1354;3.726E+01 +1355;3.734E+01 +1356;3.742E+01 +1357;3.752E+01 +1358;3.762E+01 +1359;3.772E+01 +1360;3.780E+01 +1361;3.788E+01 +1362;3.795E+01 +1363;3.804E+01 +1364;3.812E+01 +1365;3.819E+01 +1366;3.824E+01 +1367;3.829E+01 +1368;3.833E+01 +1369;3.837E+01 +1370;3.842E+01 +1371;3.846E+01 +1372;3.849E+01 +1373;3.853E+01 +1374;3.856E+01 +1375;3.859E+01 +1376;3.864E+01 +1377;3.871E+01 +1378;3.878E+01 +1379;3.884E+01 +1380;3.887E+01 +1381;3.888E+01 +1382;3.889E+01 +1383;3.892E+01 +1384;3.898E+01 +1385;3.904E+01 +1386;3.908E+01 +1387;3.912E+01 +1388;3.916E+01 +1389;3.922E+01 +1390;3.930E+01 +1391;3.938E+01 +1392;3.946E+01 +1393;3.954E+01 +1394;3.961E+01 +1395;3.966E+01 +1396;3.971E+01 +1397;3.978E+01 +1398;3.984E+01 +1399;3.987E+01 +1400;3.986E+01 +1401;3.985E+01 +1402;3.987E+01 +1403;3.992E+01 +1404;4.000E+01 +1405;4.009E+01 +1406;4.017E+01 +1407;4.022E+01 +1408;4.026E+01 +1409;4.030E+01 +1410;4.036E+01 +1411;4.042E+01 +1412;4.045E+01 +1413;4.046E+01 +1414;4.047E+01 +1415;4.050E+01 +1416;4.053E+01 +1417;4.058E+01 +1418;4.065E+01 +1419;4.070E+01 +1420;4.073E+01 +1421;4.077E+01 +1422;4.085E+01 +1423;4.095E+01 +1424;4.103E+01 +1425;4.108E+01 +1426;4.110E+01 +1427;4.112E+01 +1428;4.114E+01 +1429;4.117E+01 +1430;4.121E+01 +1431;4.127E+01 +1432;4.133E+01 +1433;4.138E+01 +1434;4.144E+01 +1435;4.151E+01 +1436;4.161E+01 +1437;4.169E+01 +1438;4.176E+01 +1439;4.182E+01 +1440;4.187E+01 +1441;4.192E+01 +1442;4.199E+01 +1443;4.207E+01 +1444;4.214E+01 +1445;4.219E+01 +1446;4.224E+01 +1447;4.230E+01 +1448;4.238E+01 +1449;4.245E+01 +1450;4.252E+01 +1451;4.260E+01 +1452;4.267E+01 +1453;4.275E+01 +1454;4.281E+01 +1455;4.286E+01 +1456;4.292E+01 +1457;4.296E+01 +1458;4.299E+01 +1459;4.303E+01 +1460;4.306E+01 +1461;4.311E+01 +1462;4.316E+01 +1463;4.321E+01 +1464;4.325E+01 +1465;4.329E+01 +1466;4.333E+01 +1467;4.337E+01 +1468;4.342E+01 +1469;4.346E+01 +1470;4.350E+01 +1471;4.356E+01 +1472;4.362E+01 +1473;4.367E+01 +1474;4.372E+01 +1475;4.375E+01 +1476;4.377E+01 +1477;4.380E+01 +1478;4.383E+01 +1479;4.387E+01 +1480;4.391E+01 +1481;4.396E+01 +1482;4.401E+01 +1483;4.405E+01 +1484;4.409E+01 +1485;4.413E+01 +1486;4.416E+01 +1487;4.420E+01 +1488;4.424E+01 +1489;4.429E+01 +1490;4.434E+01 +1491;4.440E+01 +1492;4.447E+01 +1493;4.453E+01 +1494;4.457E+01 +1495;4.461E+01 +1496;4.465E+01 +1497;4.470E+01 +1498;4.475E+01 +1499;4.482E+01 +1500;4.489E+01 +1501;4.495E+01 +1502;4.502E+01 +1503;4.508E+01 +1504;4.513E+01 +1505;4.519E+01 +1506;4.525E+01 +1507;4.531E+01 +1508;4.537E+01 +1509;4.543E+01 +1510;4.549E+01 +1511;4.554E+01 +1512;4.558E+01 +1513;4.562E+01 +1514;4.565E+01 +1515;4.567E+01 +1516;4.570E+01 +1517;4.573E+01 +1518;4.576E+01 +1519;4.580E+01 +1520;4.585E+01 +1521;4.589E+01 +1522;4.592E+01 +1523;4.595E+01 +1524;4.597E+01 +1525;4.599E+01 +1526;4.601E+01 +1527;4.604E+01 +1528;4.607E+01 +1529;4.610E+01 +1530;4.612E+01 +1531;4.615E+01 +1532;4.617E+01 +1533;4.619E+01 +1534;4.622E+01 +1535;4.625E+01 +1536;4.627E+01 +1537;4.630E+01 +1538;4.633E+01 +1539;4.637E+01 +1540;4.641E+01 +1541;4.645E+01 +1542;4.648E+01 +1543;4.650E+01 +1544;4.651E+01 +1545;4.652E+01 +1546;4.655E+01 +1547;4.657E+01 +1548;4.660E+01 +1549;4.664E+01 +1550;4.668E+01 +1551;4.671E+01 +1552;4.674E+01 +1553;4.677E+01 +1554;4.679E+01 +1555;4.682E+01 +1556;4.685E+01 +1557;4.688E+01 +1558;4.692E+01 +1559;4.695E+01 +1560;4.699E+01 +1561;4.702E+01 +1562;4.704E+01 +1563;4.706E+01 +1564;4.708E+01 +1565;4.711E+01 +1566;4.715E+01 +1567;4.719E+01 +1568;4.723E+01 +1569;4.726E+01 +1570;4.729E+01 +1571;4.733E+01 +1572;4.735E+01 +1573;4.736E+01 +1574;4.737E+01 +1575;4.739E+01 +1576;4.741E+01 +1577;4.744E+01 +1578;4.747E+01 +1579;4.751E+01 +1580;4.753E+01 +1581;4.755E+01 +1582;4.755E+01 +1583;4.756E+01 +1584;4.757E+01 +1585;4.759E+01 +1586;4.762E+01 +1587;4.765E+01 +1588;4.767E+01 +1589;4.768E+01 +1590;4.769E+01 +1591;4.770E+01 +1592;4.772E+01 +1593;4.773E+01 +1594;4.774E+01 +1595;4.775E+01 +1596;4.776E+01 +1597;4.779E+01 +1598;4.781E+01 +1599;4.784E+01 +1600;4.785E+01 +1601;4.786E+01 +1602;4.787E+01 +1603;4.789E+01 +1604;4.792E+01 +1605;4.796E+01 +1606;4.800E+01 +1607;4.803E+01 +1608;4.804E+01 +1609;4.805E+01 +1610;4.805E+01 +1611;4.806E+01 +1612;4.807E+01 +1613;4.808E+01 +1614;4.809E+01 +1615;4.810E+01 +1616;4.811E+01 +1617;4.813E+01 +1618;4.814E+01 +1619;4.816E+01 +1620;4.817E+01 +1621;4.819E+01 +1622;4.820E+01 +1623;4.821E+01 +1624;4.823E+01 +1625;4.824E+01 +1626;4.824E+01 +1627;4.822E+01 +1628;4.821E+01 +1629;4.819E+01 +1630;4.819E+01 +1631;4.820E+01 +1632;4.822E+01 +1633;4.825E+01 +1634;4.828E+01 +1635;4.830E+01 +1636;4.832E+01 +1637;4.834E+01 +1638;4.837E+01 +1639;4.839E+01 +1640;4.841E+01 +1641;4.843E+01 +1642;4.845E+01 +1643;4.846E+01 +1644;4.847E+01 +1645;4.847E+01 +1646;4.848E+01 +1647;4.849E+01 +1648;4.850E+01 +1649;4.851E+01 +1650;4.854E+01 +1651;4.856E+01 +1652;4.858E+01 +1653;4.860E+01 +1654;4.861E+01 +1655;4.862E+01 +1656;4.863E+01 +1657;4.864E+01 +1658;4.865E+01 +1659;4.866E+01 +1660;4.866E+01 +1661;4.865E+01 +1662;4.864E+01 +1663;4.863E+01 +1664;4.863E+01 +1665;4.863E+01 +1666;4.864E+01 +1667;4.865E+01 +1668;4.865E+01 +1669;4.866E+01 +1670;4.867E+01 +1671;4.869E+01 +1672;4.871E+01 +1673;4.873E+01 +1674;4.875E+01 +1675;4.876E+01 +1676;4.877E+01 +1677;4.877E+01 +1678;4.877E+01 +1679;4.877E+01 +1680;4.877E+01 +1681;4.878E+01 +1682;4.880E+01 +1683;4.881E+01 +1684;4.884E+01 +1685;4.887E+01 +1686;4.889E+01 +1687;4.891E+01 +1688;4.892E+01 +1689;4.893E+01 +1690;4.894E+01 +1691;4.896E+01 +1692;4.897E+01 +1693;4.897E+01 +1694;4.895E+01 +1695;4.893E+01 +1696;4.891E+01 +1697;4.889E+01 +1698;4.888E+01 +1699;4.887E+01 +1700;4.887E+01 +1701;4.888E+01 +1702;4.889E+01 +1703;4.889E+01 +1704;4.888E+01 +1705;4.886E+01 +1706;4.884E+01 +1707;4.883E+01 +1708;4.881E+01 +1709;4.880E+01 +1710;4.878E+01 +1711;4.877E+01 +1712;4.876E+01 +1713;4.874E+01 +1714;4.872E+01 +1715;4.870E+01 +1716;4.870E+01 +1717;4.871E+01 +1718;4.873E+01 +1719;4.875E+01 +1720;4.876E+01 +1721;4.877E+01 +1722;4.878E+01 +1723;4.878E+01 +1724;4.877E+01 +1725;4.875E+01 +1726;4.873E+01 +1727;4.872E+01 +1728;4.871E+01 +1729;4.871E+01 +1730;4.871E+01 +1731;4.872E+01 +1732;4.873E+01 +1733;4.875E+01 +1734;4.877E+01 +1735;4.879E+01 +1736;4.882E+01 +1737;4.884E+01 +1738;4.885E+01 +1739;4.885E+01 +1740;4.884E+01 +1741;4.882E+01 +1742;4.880E+01 +1743;4.878E+01 +1744;4.877E+01 +1745;4.875E+01 +1746;4.874E+01 +1747;4.874E+01 +1748;4.876E+01 +1749;4.878E+01 +1750;4.881E+01 +1751;4.884E+01 +1752;4.888E+01 +1753;4.890E+01 +1754;4.891E+01 +1755;4.891E+01 +1756;4.889E+01 +1757;4.888E+01 +1758;4.886E+01 +1759;4.885E+01 +1760;4.884E+01 +1761;4.883E+01 +1762;4.883E+01 +1763;4.884E+01 +1764;4.884E+01 +1765;4.885E+01 +1766;4.887E+01 +1767;4.889E+01 +1768;4.892E+01 +1769;4.895E+01 +1770;4.897E+01 +1771;4.899E+01 +1772;4.900E+01 +1773;4.900E+01 +1774;4.898E+01 +1775;4.895E+01 +1776;4.892E+01 +1777;4.890E+01 +1778;4.890E+01 +1779;4.890E+01 +1780;4.890E+01 +1781;4.890E+01 +1782;4.890E+01 +1783;4.891E+01 +1784;4.893E+01 +1785;4.895E+01 +1786;4.898E+01 +1787;4.900E+01 +1788;4.903E+01 +1789;4.905E+01 +1790;4.906E+01 +1791;4.906E+01 +1792;4.906E+01 +1793;4.905E+01 +1794;4.904E+01 +1795;4.904E+01 +1796;4.906E+01 +1797;4.909E+01 +1798;4.914E+01 +1799;4.920E+01 +1800;4.924E+01 +1801;4.928E+01 +1802;4.932E+01 +1803;4.936E+01 +1804;4.941E+01 +1805;4.944E+01 +1806;4.947E+01 +1807;4.949E+01 +1808;4.950E+01 +1809;4.953E+01 +1810;4.954E+01 +1811;4.954E+01 +1812;4.955E+01 +1813;4.956E+01 +1814;4.958E+01 +1815;4.961E+01 +1816;4.965E+01 +1817;4.970E+01 +1818;4.974E+01 +1819;4.975E+01 +1820;4.974E+01 +1821;4.971E+01 +1822;4.970E+01 +1823;4.971E+01 +1824;4.973E+01 +1825;4.974E+01 +1826;4.974E+01 +1827;4.973E+01 +1828;4.973E+01 +1829;4.974E+01 +1830;4.975E+01 +1831;4.976E+01 +1832;4.978E+01 +1833;4.981E+01 +1834;4.986E+01 +1835;4.990E+01 +1836;4.993E+01 +1837;4.994E+01 +1838;4.993E+01 +1839;4.989E+01 +1840;4.982E+01 +1841;4.978E+01 +1842;4.979E+01 +1843;4.987E+01 +1844;4.997E+01 +1845;5.003E+01 +1846;5.002E+01 +1847;4.998E+01 +1848;4.995E+01 +1849;4.996E+01 +1850;4.999E+01 +1851;5.002E+01 +1852;5.006E+01 +1853;5.008E+01 +1854;5.010E+01 +1855;5.009E+01 +1856;5.007E+01 +1857;5.005E+01 +1858;5.004E+01 +1859;5.003E+01 +1860;5.004E+01 +1861;5.007E+01 +1862;5.014E+01 +1863;5.025E+01 +1864;5.035E+01 +1865;5.039E+01 +1866;5.036E+01 +1867;5.028E+01 +1868;5.024E+01 +1869;5.026E+01 +1870;5.033E+01 +1871;5.038E+01 +1872;5.037E+01 +1873;5.033E+01 +1874;5.027E+01 +1875;5.024E+01 +1876;5.021E+01 +1877;5.020E+01 +1878;5.021E+01 +1879;5.024E+01 +1880;5.026E+01 +1881;5.029E+01 +1882;5.032E+01 +1883;5.033E+01 +1884;5.033E+01 +1885;5.030E+01 +1886;5.025E+01 +1887;5.020E+01 +1888;5.017E+01 +1889;5.017E+01 +1890;5.019E+01 +1891;5.021E+01 +1892;5.021E+01 +1893;5.022E+01 +1894;5.026E+01 +1895;5.034E+01 +1896;5.045E+01 +1897;5.054E+01 +1898;5.060E+01 +1899;5.064E+01 +1900;5.067E+01 +1901;5.070E+01 +1902;5.071E+01 +1903;5.072E+01 +1904;5.071E+01 +1905;5.070E+01 +1906;5.070E+01 +1907;5.072E+01 +1908;5.074E+01 +1909;5.078E+01 +1910;5.083E+01 +1911;5.085E+01 +1912;5.086E+01 +1913;5.088E+01 +1914;5.092E+01 +1915;5.096E+01 +1916;5.099E+01 +1917;5.098E+01 +1918;5.093E+01 +1919;5.085E+01 +1920;5.080E+01 +1921;5.077E+01 +1922;5.077E+01 +1923;5.078E+01 +1924;5.078E+01 +1925;5.077E+01 +1926;5.076E+01 +1927;5.077E+01 +1928;5.080E+01 +1929;5.083E+01 +1930;5.085E+01 +1931;5.086E+01 +1932;5.084E+01 +1933;5.082E+01 +1934;5.082E+01 +1935;5.084E+01 +1936;5.086E+01 +1937;5.087E+01 +1938;5.084E+01 +1939;5.081E+01 +1940;5.078E+01 +1941;5.079E+01 +1942;5.083E+01 +1943;5.088E+01 +1944;5.092E+01 +1945;5.096E+01 +1946;5.098E+01 +1947;5.101E+01 +1948;5.104E+01 +1949;5.108E+01 +1950;5.112E+01 +1951;5.114E+01 +1952;5.117E+01 +1953;5.120E+01 +1954;5.123E+01 +1955;5.126E+01 +1956;5.130E+01 +1957;5.133E+01 +1958;5.136E+01 +1959;5.139E+01 +1960;5.143E+01 +1961;5.147E+01 +1962;5.153E+01 +1963;5.159E+01 +1964;5.164E+01 +1965;5.167E+01 +1966;5.169E+01 +1967;5.170E+01 +1968;5.172E+01 +1969;5.174E+01 +1970;5.177E+01 +1971;5.181E+01 +1972;5.183E+01 +1973;5.185E+01 +1974;5.186E+01 +1975;5.188E+01 +1976;5.190E+01 +1977;5.194E+01 +1978;5.198E+01 +1979;5.202E+01 +1980;5.206E+01 +1981;5.209E+01 +1982;5.212E+01 +1983;5.214E+01 +1984;5.216E+01 +1985;5.217E+01 +1986;5.218E+01 +1987;5.220E+01 +1988;5.222E+01 +1989;5.223E+01 +1990;5.224E+01 +1991;5.225E+01 +1992;5.225E+01 +1993;5.226E+01 +1994;5.227E+01 +1995;5.228E+01 +1996;5.229E+01 +1997;5.230E+01 +1998;5.229E+01 +1999;5.228E+01 +2000.065834;5.239E+01 +2009.942668;5.268E+01 +2020.098566;5.272E+01 +2029.969298;5.296E+01 +2039.837;5.308E+01 +2049.98346;5.332E+01 +2059.844971;5.353E+01 +2069.985031;5.379E+01 +2079.840289;5.400E+01 +2089.973887;5.420E+01 +2099.822832;5.439E+01 +2109.949906;5.463E+01 +2119.792477;5.471E+01 +2129.912964;5.475E+01 +2140.030088;5.475E+01 +2149.862941;5.486E+01 +2159.973384;5.485E+01 +2169.799712;5.490E+01 +2179.903413;5.498E+01 +2190.003671;5.511E+01 +2199.820052;5.519E+01 +2209.913475;5.527E+01 +2219.723179;5.541E+01 +2229.809704;5.547E+01 +2239.892708;5.558E+01 +2249.692237;5.563E+01 +2259.76825;5.596E+01 +2269.840694;5.624E+01 +2279.909553;5.648E+01 +2289.695271;5.652E+01 +2299.757015;5.653E+01 +2309.815127;5.667E+01 +2319.869592;5.686E+01 +2329.641254;5.706E+01 +2339.688478;5.713E+01 +2349.732008;5.716E+01 +2359.771828;5.727E+01 +2369.80792;5.728E+01 +2379.840271;5.720E+01 +2389.590342;5.704E+01 +2399.615266;5.699E+01 +2409.6364;5.699E+01 +2419.65373;5.684E+01 +2429.667238;5.682E+01 +2439.676909;5.682E+01 +2449.682728;5.694E+01 +2459.684679;5.700E+01 +2469.682745;5.715E+01 +2479.676911;5.719E+01 +2489.667162;5.716E+01 +2499.653481;5.701E+01 +2509.635853;5.696E+01 +2519.614261;5.689E+01 +2529.588691;5.691E+01 +2539.559127;5.684E+01 +2549.525552;5.681E+01 +2559.487951;5.670E+01 +2569.722871;5.686E+01 +2579.677058;5.696E+01 +2589.627171;5.681E+01 +2599.573195;5.660E+01 +2609.515113;5.651E+01 +2619.452911;5.675E+01 +2629.662448;5.684E+01 +2639.591842;5.681E+01 +2649.517067;5.676E+01 +2659.438109;5.667E+01 +2669.630358;5.644E+01 +2679.542867;5.601E+01 +2689.451145;5.518E+01 +2699.630227;5.391E+01 +2709.529877;5.149E+01 +2719.425248;4.737E+01 +2729.591015;4.061E+01 +2739.477662;3.252E+01 +2749.359982;2.426E+01 +2759.512287;1.788E+01 +2769.385788;1.295E+01 +2779.528995;9.824E+00 +2789.393611;7.738E+00 +2799.527654;6.652E+00 +2809.383323;5.967E+00 +2819.508136;5.483E+00 +2829.354792;5.245E+00 +2839.47031;5.021E+00 +2849.30789;4.891E+00 +2859.414046;4.743E+00 +2869.515434;4.787E+00 +2879.339216;4.920E+00 +2889.431143;5.091E+00 +2899.24569;5.304E+00 +2909.328092;5.424E+00 +2919.40564;5.607E+00 +2929.206149;5.844E+00 +2939.274073;6.160E+00 +2949.337095;6.416E+00 +2959.395197;6.595E+00 +2969.17672;6.921E+00 +2979.225066;7.271E+00 +2989.268442;7.631E+00 +2999.306833;8.016E+00 +3009.34022;8.206E+00 +3019.368587;8.588E+00 +3029.391918;8.726E+00 +3039.139497;9.211E+00 +3049.152842;9.450E+00 +3059.1611;9.860E+00 +3069.164256;1.015E+01 +3079.162292;1.050E+01 +3089.155191;1.085E+01 +3099.142938;1.119E+01 +3109.125515;1.143E+01 +3119.102905;1.188E+01 +3129.075093;1.229E+01 +3139.311366;1.276E+01 +3149.272956;1.323E+01 +3159.229293;1.372E+01 +3169.18036;1.428E+01 +3179.126141;1.467E+01 +3189.066618;1.512E+01 +3199.27022;1.545E+01 +3209.199897;1.569E+01 +3219.124221;1.600E+01 +3229.043176;1.650E+01 +3239.224603;1.700E+01 +3249.132622;1.732E+01 +3259.035221;1.748E+01 +3269.199799;1.774E+01 +3279.091361;1.787E+01 +3288.977454;1.799E+01 +3299.125027;1.795E+01 +3308.999981;1.775E+01 +3319.136081;1.745E+01 +3328.999829;1.701E+01 +3339.124385;1.651E+01 +3348.976861;1.603E+01 +3359.089804;1.533E+01 +3368.930939;1.476E+01 +3379.0322;1.394E+01 +3389.127517;1.310E+01 +3398.951437;1.198E+01 +3409.034967;1.084E+01 +3419.112499;9.678E+00 +3428.919052;8.687E+00 +3438.984693;7.682E+00 +3449.044284;6.747E+00 +3458.833318;5.903E+00 +3468.880914;5.274E+00 +3478.922407;4.772E+00 +3488.957779;4.122E+00 +3498.987012;3.509E+00 +3509.010089;2.762E+00 +3519.026992;2.259E+00 +3529.037703;1.823E+00 +3538.779008;1.525E+00 +3548.777448;1.166E+00 +3558.769643;1.024E+00 +3568.755577;9.574E-01 +3578.997769;8.092E-01 +3588.970961;5.896E-01 +3598.937838;4.913E-01 +3608.898383;5.280E-01 +3618.852578;3.674E-01 +3628.800407;3.497E-01 +3638.74185;4.225E-01 +3648.938253;4.881E-01 +3658.866705;6.658E-01 +3668.788719;5.211E-01 +3678.704279;6.675E-01 +3688.874043;3.899E-01 +3698.776469;6.638E-01 +3708.672387;6.883E-01 +3718.821938;1.100E+00 +3728.704616;9.438E-01 +3738.840542;1.003E+00 +3748.709907;8.544E-01 +3758.832135;1.155E+00 +3768.688117;1.370E+00 +3778.796573;1.508E+00 +3788.639101;1.748E+00 +3798.733711;2.069E+00 +3808.821282;2.524E+00 +3818.643407;2.780E+00 +3828.717023;2.681E+00 +3838.783543;2.734E+00 +3848.585104;2.616E+00 +3858.637559;2.802E+00 +3868.682864;2.618E+00 +3878.720999;2.636E+00 +3888.751945;2.293E+00 +3898.518755;2.111E+00 +3908.535455;1.794E+00 +3918.54491;1.915E+00 +3928.547104;1.831E+00 +3938.542016;1.646E+00 +3948.52963;1.324E+00 +3958.509925;9.924E-01 +3968.482884;1.389E+00 +3978.70392;1.423E+00 +3988.661962;1.633E+00 +3998.612612;1.213E+00 +4008.555852;1.041E+00 +4018.491662;1.109E+00 +4028.6745;1.163E+00 +4038.595205;1.336E+00 +4048.508426;1.228E+00 +4058.414143;1.197E+00 +4068.56604;1.197E+00 +4078.456503;1.181E+00 +4088.592715;1.236E+00 +4098.467848;1.319E+00 +4108.588299;1.330E+00 +4118.448027;1.323E+00 +4128.552639;1.282E+00 +4138.396887;1.279E+00 +4148.485584;1.249E+00 +4158.566193;1.256E+00 +4168.386981;1.258E+00 +4178.45156;1.285E+00 +4188.507992;1.274E+00 +4198.305151;1.270E+00 +4208.345437;1.258E+00 +4218.377519;1.257E+00 +4228.401377;1.228E+00 +4238.416991;1.165E+00 +4248.424343;1.156E+00 +4258.423411;1.110E+00 +4268.414178;1.071E+00 +4278.396623;1.029E+00 +4288.370728;1.024E+00 +4298.336471;1.012E+00 +4308.293836;9.872E-01 +4318.2428;9.623E-01 +4328.431752;9.361E-01 +4338.363649;9.032E-01 +4348.287088;8.727E-01 +4358.20205;8.536E-01 +4368.356068;8.444E-01 +4378.253805;8.409E-01 +4388.390126;8.330E-01 +4398.270558;8.178E-01 +4408.389101;7.947E-01 +4418.252149;7.762E-01 +4428.352834;7.557E-01 +4438.198419;7.400E-01 +4448.281165;7.262E-01 +4458.354799;7.086E-01 +4468.173935;6.932E-01 +4478.229508;6.785E-01 +4488.275909;6.645E-01 +4498.313117;6.517E-01 +4508.096635;6.391E-01 +4518.115621;6.272E-01 +4528.125352;6.159E-01 +4538.125809;6.053E-01 +4548.11697;5.953E-01 +4558.098816;5.859E-01 +4568.071325;5.769E-01 +4578.277365;5.682E-01 +4588.230912;5.599E-01 +4598.175061;5.518E-01 +4608.109792;5.442E-01 +4618.035085;5.369E-01 +4628.192649;5.299E-01 +4638.098772;5.233E-01 +4647.995395;5.170E-01 +4658.123528;5.110E-01 +4668.000857;5.052E-01 +4678.109183;4.997E-01 +4687.967136;4.944E-01 +4698.05557;4.892E-01 +4708.133906;4.843E-01 +4717.962521;4.796E-01 +4728.020838;4.751E-01 +4738.068993;4.707E-01 +4748.106964;4.665E-01 +4758.13473;4.625E-01 +4767.913874;4.586E-01 +4777.921408;4.548E-01 +4787.918673;4.511E-01 +4797.905646;4.475E-01 +4807.882307;4.441E-01 +4818.085802;4.407E-01 +4828.041526;4.374E-01 +4837.986874;4.342E-01 +4847.921823;4.311E-01 +4857.846351;4.281E-01 +4867.996361;4.252E-01 +4877.899737;4.223E-01 +4888.028045;4.195E-01 +4897.910179;4.167E-01 +4908.016697;4.140E-01 +4917.877504;4.114E-01 +4927.962141;4.089E-01 +4937.801534;4.064E-01 +4947.864203;4.039E-01 +4957.915724;4.015E-01 +4967.956075;3.992E-01 +4977.985234;3.969E-01 +4987.77033;3.947E-01 +4997.777298;3.925E-01 +5007.776688;3.903E-01 +5017.595804;3.882E-01 +5028.169082;3.862E-01 +5037.98605;3.841E-01 +5047.80198;3.821E-01 +5057.616872;3.802E-01 +5068.18559;3.782E-01 +5077.998316;3.764E-01 +5087.809996;3.745E-01 +5097.620628;3.727E-01 +5107.430211;3.709E-01 +5117.993201;3.691E-01 +5127.800597;3.674E-01 +5137.606938;3.657E-01 +5147.41222;3.640E-01 +5157.970569;3.623E-01 +5167.773648;3.607E-01 +5177.575662;3.591E-01 +5187.376611;3.575E-01 +5197.930283;3.560E-01 +5207.729011;3.545E-01 +5217.526666;3.529E-01 +5227.323247;3.515E-01 +5237.872207;3.500E-01 +5247.666549;3.486E-01 +5257.459812;3.471E-01 +5268.005191;3.457E-01 +5277.796202;3.444E-01 +5287.586127;3.430E-01 +5297.374963;3.417E-01 +5307.915566;3.404E-01 +5317.702134;3.391E-01 +5327.487607;3.378E-01 +5337.271982;3.365E-01 +5347.807773;3.353E-01 +5357.589864;3.340E-01 +5367.370852;3.328E-01 +5377.902987;3.316E-01 +5387.681677;3.305E-01 +5397.459258;3.293E-01 +5407.235727;3.281E-01 +5417.762987;3.270E-01 +5427.537141;3.259E-01 +5437.310177;3.248E-01 +5447.833734;3.237E-01 +5457.604442;3.226E-01 +5467.374028;3.216E-01 +5477.142487;3.205E-01 +5487.661104;3.195E-01 +5497.427219;3.184E-01 +5507.192201;3.174E-01 +5517.707068;3.164E-01 +5527.469693;3.154E-01 +5537.231179;3.145E-01 +5547.742274;3.135E-01 +5557.50139;3.125E-01 +5567.259362;3.116E-01 +5577.766665;3.107E-01 +5587.522253;3.097E-01 +5597.276691;3.088E-01 +5607.780182;3.079E-01 +5617.532223;3.070E-01 +5627.283108;3.062E-01 +5637.032835;3.053E-01 +5647.531242;3.044E-01 +5657.278555;3.036E-01 +5667.024703;3.027E-01 +5677.51925;3.019E-01 +5687.262972;3.011E-01 +5697.005523;3.003E-01 +5707.496189;2.995E-01 +5717.236302;2.987E-01 +5726.975237;2.979E-01 +5737.462002;2.971E-01 +5747.198485;2.963E-01 +5757.682605;2.956E-01 +5767.416629;2.948E-01 +5777.149464;2.941E-01 +5787.629649;2.934E-01 +5797.360012;2.926E-01 +5807.08918;2.919E-01 +5817.565409;2.912E-01 +5827.292092;2.905E-01 +5837.017575;2.898E-01 +5847.489828;2.891E-01 +5857.212813;2.884E-01 +5866.934591;2.878E-01 +5877.402847;2.871E-01 +5887.122114;2.864E-01 +5897.587661;2.858E-01 +5907.304408;2.851E-01 +5917.019938;2.845E-01 +5927.481454;2.839E-01 +5937.194452;2.833E-01 +5946.906226;2.826E-01 +5957.363691;2.820E-01 +5967.072921;2.814E-01 +5977.52764;2.808E-01 +5987.234315;2.802E-01 +5996.939757;2.797E-01 +6007.39039;2.791E-01 +6017.093265;2.785E-01 +6026.794901;2.779E-01 +6037.241429;2.774E-01 +6046.940485;2.768E-01 +6057.38423;2.763E-01 +6067.080698;2.757E-01 +6076.775917;2.752E-01 +6087.215522;2.747E-01 +6096.908139;2.741E-01 +6107.344938;2.736E-01 +6117.034946;2.731E-01 +6126.723694;2.726E-01 +6137.156319;2.721E-01 +6146.842444;2.716E-01 +6157.272241;2.711E-01 +6166.955735;2.706E-01 +6177.382693;2.701E-01 +6187.063548;2.697E-01 +6196.743128;2.692E-01 +6207.165865;2.687E-01 +6216.842793;2.683E-01 +6227.262668;2.678E-01 +6236.936935;2.673E-01 +6246.609918;2.669E-01 +6257.025537;2.664E-01 +6266.695846;2.660E-01 +6277.108582;2.656E-01 +6286.776208;2.651E-01 +6297.18605;2.647E-01 +6306.850986;2.643E-01 +6317.257926;2.639E-01 +6326.920162;2.634E-01 +6336.581095;2.630E-01 +6346.983717;2.626E-01 +6356.641938;2.622E-01 +6367.041635;2.618E-01 +6376.697135;2.614E-01 +6387.093897;2.610E-01 +6396.746668;2.606E-01 +6407.140486;2.602E-01 +6416.790518;2.599E-01 +6427.181383;2.595E-01 +6436.828669;2.591E-01 +6446.47463;2.587E-01 +6456.861103;2.584E-01 +6466.504304;2.580E-01 +6476.8878;2.576E-01 +6486.528234;2.573E-01 +6496.908745;2.569E-01 +6506.546402;2.566E-01 +6516.923919;2.562E-01 +6526.558791;2.559E-01 +6536.933304;2.555E-01 +6546.565382;2.552E-01 +6556.936882;2.548E-01 +6566.566159;2.545E-01 +6576.934637;2.542E-01 +6586.561102;2.539E-01 +6596.926549;2.535E-01 +6606.550195;2.532E-01 +6616.912601;2.529E-01 +6626.53342;2.526E-01 +6636.892776;2.523E-01 +6646.510758;2.519E-01 +6656.867055;2.516E-01 +6666.482193;2.513E-01 +6676.835422;2.510E-01 +6686.447706;2.507E-01 +6696.797857;2.504E-01 +6706.40728;2.501E-01 +6716.754344;2.498E-01 +6726.360896;2.496E-01 +6736.704865;2.493E-01 +6746.308538;2.490E-01 +6756.649402;2.487E-01 +6766.250188;2.484E-01 +6776.587937;2.482E-01 +6786.924068;2.479E-01 +6796.520453;2.476E-01 +6806.853456;2.473E-01 +6816.446931;2.471E-01 +6826.776797;2.468E-01 +6836.367355;2.465E-01 +6846.694074;2.463E-01 +6856.281707;2.460E-01 +6866.60527;2.458E-01 +6876.189968;2.455E-01 +6886.510366;2.453E-01 +6896.82912;2.450E-01 +6906.409345;2.448E-01 +6916.724921;2.445E-01 +6926.30219;2.443E-01 +6936.614577;2.440E-01 +6946.188882;2.438E-01 +6956.498072;2.436E-01 +6966.805601;2.433E-01 +6976.375388;2.431E-01 +6986.679706;2.429E-01 +6996.246507;2.426E-01 +7006.547605;2.424E-01 +7016.111412;2.422E-01 +7026.40928;2.420E-01 +7036.70547;2.418E-01 +7046.264714;2.415E-01 +7056.557661;2.413E-01 +7066.113889;2.411E-01 +7076.403584;2.409E-01 +7086.691589;2.407E-01 +7096.243221;2.405E-01 +7106.527959;2.403E-01 +7116.076554;2.400E-01 +7126.358018;2.398E-01 +7136.637779;2.396E-01 +7146.181746;2.394E-01 +7156.458218;2.392E-01 +7165.999126;2.390E-01 +7176.2723;2.388E-01 +7186.54376;2.386E-01 +7196.080008;2.384E-01 +7206.348156;2.383E-01 +7216.614582;2.381E-01 +7226.14615;2.379E-01 +7236.409251;2.377E-01 +7245.937725;2.375E-01 +7256.197491;2.373E-01 +7266.455524;2.371E-01 +7275.979286;2.369E-01 +7286.23397;2.368E-01 +7296.486914;2.366E-01 +7306.005944;2.364E-01 +7316.255525;2.362E-01 +7326.503358;2.361E-01 +7336.017637;2.359E-01 +7346.262094;2.357E-01 +7356.504797;2.355E-01 +7366.014304;2.354E-01 +7376.253616;2.352E-01 +7386.491167;2.350E-01 +7395.995884;2.349E-01 +7406.23003;2.347E-01 +7416.462408;2.345E-01 +7425.962315;2.344E-01 +7436.191274;2.342E-01 +7446.418458;2.340E-01 +7455.913536;2.339E-01 +7466.137287;2.337E-01 +7476.359256;2.336E-01 +7485.849485;2.334E-01 +7496.068008;2.332E-01 +7506.284741;2.331E-01 +7515.770101;2.329E-01 +7525.983375;2.328E-01 +7536.19485;2.326E-01 +7545.675324;2.325E-01 +7555.883326;2.323E-01 +7566.089524;2.322E-01 +7576.293916;2.320E-01 +7585.767802;2.319E-01 +7595.968701;2.317E-01 +7606.167786;2.316E-01 +7615.636739;2.314E-01 +7625.832319;2.313E-01 +7636.026078;2.312E-01 +7646.218013;2.310E-01 +7655.680318;2.309E-01 +7665.868729;2.307E-01 +7676.05531;2.306E-01 +7686.240057;2.305E-01 +7695.695679;2.303E-01 +7705.876884;2.302E-01 +7716.056248;2.301E-01 +7726.23377;2.299E-01 +7735.682674;2.298E-01 +7745.856636;2.296E-01 +7756.028747;2.295E-01 +7766.199006;2.294E-01 +7775.641157;2.293E-01 +7785.807837;2.291E-01 +7795.972657;2.290E-01 +7806.135616;2.289E-01 +7815.57098;2.287E-01 +7825.730341;2.286E-01 +7835.887833;2.285E-01 +7846.043454;2.284E-01 +7855.471995;2.282E-01 +7865.624;2.281E-01 +7875.774127;2.280E-01 +7885.922372;2.279E-01 +7896.068734;2.278E-01 +7905.488668;2.276E-01 +7915.631391;2.275E-01 +7925.772224;2.274E-01 +7935.911164;2.273E-01 +7946.048209;2.272E-01 +7955.45948;2.270E-01 +7965.592863;2.269E-01 +7975.724343;2.268E-01 +7985.853919;2.267E-01 +7995.981587;2.266E-01 +8005.384141;2.265E-01 +8015.508125;2.264E-01 +8025.630194;2.262E-01 +8035.750346;2.261E-01 +8045.86858;2.260E-01 +8055.984892;2.259E-01 +8065.376887;2.258E-01 +8075.489487;2.257E-01 +8085.600158;2.256E-01 +8095.708897;2.255E-01 +8105.815704;2.254E-01 +8115.920574;2.253E-01 +8125.301933;2.252E-01 +8135.403063;2.251E-01 +8145.502251;2.250E-01 +8155.599493;2.248E-01 +8165.694787;2.247E-01 +8175.788131;2.246E-01 +8185.879523;2.245E-01 +8195.248351;2.244E-01 +8205.33597;2.243E-01 +8215.42163;2.242E-01 +8225.505327;2.241E-01 +8235.587061;2.240E-01 +8245.666827;2.239E-01 +8255.744624;2.238E-01 +8265.82045;2.237E-01 +8275.174806;2.236E-01 +8285.246822;2.236E-01 +8295.31686;2.235E-01 +8305.384917;2.234E-01 +8315.45099;2.233E-01 +8325.515077;2.232E-01 +8335.577176;2.231E-01 +8345.637284;2.230E-01 +8355.695399;2.229E-01 +8365.751519;2.228E-01 +8375.087555;2.227E-01 +8385.139819;2.226E-01 +8395.190081;2.225E-01 +8405.238338;2.224E-01 +8415.284587;2.223E-01 +8425.328827;2.223E-01 +8435.371055;2.222E-01 +8445.411268;2.221E-01 +8455.449464;2.220E-01 +8465.485641;2.219E-01 +8475.519796;2.218E-01 +8485.551927;2.217E-01 +8495.582031;2.216E-01 +8505.610107;2.216E-01 +8515.636151;2.215E-01 +8525.660162;2.214E-01 +8535.682136;2.213E-01 +8544.98643;2.212E-01 +8555.004471;2.211E-01 +8565.020468;2.210E-01 +8575.034421;2.210E-01 +8585.046325;2.209E-01 +8595.056179;2.208E-01 +8605.063981;2.207E-01 +8615.069727;2.206E-01 +8625.073416;2.205E-01 +8635.075045;2.205E-01 +8645.074612;2.204E-01 +8655.072114;2.203E-01 +8665.067549;2.202E-01 +8675.060915;2.201E-01 +8685.052209;2.201E-01 +8695.041429;2.200E-01 +8705.028573;2.199E-01 +8715.013637;2.198E-01 +8724.99662;2.198E-01 +8734.977519;2.197E-01 +8744.956333;2.196E-01 +8754.933058;2.195E-01 +8764.907692;2.195E-01 +8774.880232;2.194E-01 +8784.850677;2.193E-01 +8794.819025;2.192E-01 +8804.785271;2.192E-01 +8815.461059;2.191E-01 +8825.422948;2.190E-01 +8835.382728;2.189E-01 +8845.340399;2.189E-01 +8855.295957;2.188E-01 +8865.249401;2.187E-01 +8875.200727;2.186E-01 +8885.149933;2.186E-01 +8895.097018;2.185E-01 +8905.041978;2.184E-01 +8914.984812;2.184E-01 +8924.925517;2.183E-01 +8934.86409;2.182E-01 +8944.800529;2.181E-01 +8954.734832;2.181E-01 +8965.376355;2.180E-01 +8975.306225;2.179E-01 +8985.233952;2.179E-01 +8995.159534;2.178E-01 +9005.082967;2.177E-01 +9015.004249;2.177E-01 +9024.923378;2.176E-01 +9034.840353;2.175E-01 +9044.755169;2.175E-01 +9054.667825;2.174E-01 +9065.286129;2.173E-01 +9075.194303;2.173E-01 +9085.10031;2.172E-01 +9095.004147;2.171E-01 +9104.905813;2.171E-01 +9114.805303;2.170E-01 +9124.702617;2.169E-01 +9134.597752;2.169E-01 +9145.197262;2.168E-01 +9155.087875;2.167E-01 +9164.976302;2.167E-01 +9174.86254;2.166E-01 +9184.746587;2.165E-01 +9194.628441;2.165E-01 +9204.508098;2.164E-01 +9215.091006;2.164E-01 +9224.966108;2.163E-01 +9234.839006;2.162E-01 +9244.709699;2.162E-01 +9254.578185;2.161E-01 +9265.149109;2.160E-01 +9275.013013;2.160E-01 +9284.874702;2.159E-01 +9294.734174;2.159E-01 +9304.591426;2.158E-01 +9314.446456;2.157E-01 +9325.002949;2.157E-01 +9334.853368;2.156E-01 +9344.701558;2.156E-01 +9354.547517;2.155E-01 +9364.391241;2.154E-01 +9374.935607;2.154E-01 +9384.774696;2.153E-01 +9394.611544;2.153E-01 +9404.446148;2.152E-01 +9414.980732;2.152E-01 +9424.810682;2.151E-01 +9434.63838;2.150E-01 +9444.463825;2.150E-01 +9454.988585;2.149E-01 +9464.809356;2.149E-01 +9474.627866;2.148E-01 +9484.444114;2.148E-01 +9494.959008;2.147E-01 +9504.770561;2.146E-01 +9514.579844;2.146E-01 +9524.386855;2.145E-01 +9534.891842;2.145E-01 +9544.694139;2.144E-01 +9554.494156;2.144E-01 +9564.291891;2.143E-01 +9574.786929;2.143E-01 +9584.57993;2.142E-01 +9594.370643;2.142E-01 +9604.858149;2.141E-01 +9614.644112;2.140E-01 +9624.427779;2.140E-01 +9634.209147;2.139E-01 +9644.686632;2.139E-01 +9654.463232;2.138E-01 +9664.237526;2.138E-01 +9674.707423;2.137E-01 +9684.476934;2.137E-01 +9694.244132;2.136E-01 +9704.706417;2.136E-01 +9714.468817;2.135E-01 +9724.228897;2.135E-01 +9734.683548;2.134E-01 +9744.438814;2.134E-01 +9754.191753;2.133E-01 +9764.638746;2.133E-01 +9774.386857;2.132E-01 +9784.132634;2.132E-01 +9794.571945;2.131E-01 +9804.312879;2.131E-01 +9814.746996;2.130E-01 +9824.483078;2.130E-01 +9834.216813;2.129E-01 +9844.643209;2.129E-01 +9854.372076;2.128E-01 +9864.09859;2.128E-01 +9874.517241;2.127E-01 +9884.238873;2.127E-01 +9894.652288;2.126E-01 +9904.369027;2.126E-01 +9914.083402;2.125E-01 +9924.489032;2.125E-01 +9934.198499;2.124E-01 +9944.598867;2.124E-01 +9954.303417;2.123E-01 +9964.00559;2.123E-01 +9974.398135;2.122E-01 +9984.095376;2.122E-01 +9994.482631;2.121E-01 +10004.17493;2.121E-01 +10014.55689;2.121E-01 +10024.24423;2.120E-01 +10033.92919;2.120E-01 +10044.30326;2.119E-01 +10053.98325;2.119E-01 +10064.352;2.118E-01 +10074.02701;2.118E-01 +10084.39043;2.117E-01 +10094.06045;2.117E-01 +10104.41852;2.116E-01 +10114.08355;2.116E-01 +10124.43625;2.116E-01 +10134.09628;2.115E-01 +10144.44362;2.115E-01 +10154.09862;2.114E-01 +10164.44058;2.114E-01 +10174.09056;2.113E-01 +10184.42713;2.113E-01 +10194.07208;2.112E-01 +10204.40325;2.112E-01 +10214.04315;2.112E-01 +10224.36891;2.111E-01 +10234.00375;2.111E-01 +10244.32409;2.110E-01 +10253.95386;2.110E-01 +10264.26877;2.109E-01 +10273.89347;2.109E-01 +10284.20293;2.109E-01 +10293.82255;2.108E-01 +10304.12656;2.108E-01 +10313.74108;2.107E-01 +10324.03963;2.107E-01 +10334.33534;2.106E-01 +10343.94211;2.106E-01 +10354.23234;2.106E-01 +10363.834;2.105E-01 +10374.11874;2.105E-01 +10383.71527;2.104E-01 +10393.99451;2.104E-01 +10404.2709;2.104E-01 +10413.85962;2.103E-01 +10424.1305;2.103E-01 +10433.71407;2.102E-01 +10443.97941;2.102E-01 +10454.2419;2.102E-01 +10463.81763;2.101E-01 +10474.07457;2.101E-01 +10483.64512;2.100E-01 +10493.8965;2.100E-01 +10504.14501;2.100E-01 +10513.70769;2.099E-01 +10523.95063;2.099E-01 +10534.19068;2.098E-01 +10543.74545;2.098E-01 +10553.97992;2.098E-01 +10564.21149;2.097E-01 +10573.75835;2.097E-01 +10583.98432;2.097E-01 +10593.52594;2.096E-01 +10603.7463;2.096E-01 +10613.96375;2.095E-01 +10624.17829;2.095E-01 +10633.70924;2.095E-01 +10643.91815;2.094E-01 +10654.12414;2.094E-01 +10663.64709;2.094E-01 +10673.84743;2.093E-01 +10684.04485;2.093E-01 +10693.55979;2.092E-01 +10703.75154;2.092E-01 +10713.94035;2.092E-01 +10723.44726;2.091E-01 +10733.6304;2.091E-01 +10743.81059;2.091E-01 +10753.98783;2.090E-01 +10763.48393;2.090E-01 +10773.65548;2.090E-01 +10783.82407;2.089E-01 +10793.9897;2.089E-01 +10803.47495;2.088E-01 +10813.63486;2.088E-01 +10823.7918;2.088E-01 +10833.94578;2.087E-01 +10843.42014;2.087E-01 +10853.56838;2.087E-01 +10863.71363;2.086E-01 +10873.85591;2.086E-01 +10883.31935;2.086E-01 +10893.45586;2.085E-01 +10903.58938;2.085E-01 +10913.71992;2.085E-01 +10923.84746;2.084E-01 +10933.29714;2.084E-01 +10943.41889;2.084E-01 +10953.53764;2.083E-01 +10963.65339;2.083E-01 +10973.76613;2.083E-01 +10983.87586;2.082E-01 +10993.3089;2.082E-01 +11003.41281;2.082E-01 +11013.5137;2.081E-01 +11023.61157;2.081E-01 +11033.70642;2.081E-01 +11043.79825;2.080E-01 +11053.21456;2.080E-01 +11063.30053;2.080E-01 +11073.38346;2.079E-01 +11083.46337;2.079E-01 +11093.54023;2.079E-01 +11103.61405;2.078E-01 +11113.68483;2.078E-01 +11123.75256;2.078E-01 +11133.14635;2.077E-01 +11143.20819;2.077E-01 +11153.26696;2.077E-01 +11163.32268;2.076E-01 +11173.37534;2.076E-01 +11183.42494;2.076E-01 +11193.47147;2.075E-01 +11203.51493;2.075E-01 +11213.55532;2.075E-01 +11223.59263;2.075E-01 +11233.62687;2.074E-01 +11243.65803;2.074E-01 +11253.6861;2.074E-01 +11263.71109;2.073E-01 +11273.06496;2.073E-01 +11283.08398;2.073E-01 +11293.09991;2.072E-01 +11303.11274;2.072E-01 +11313.12247;2.072E-01 +11323.12909;2.071E-01 +11333.13262;2.071E-01 +11343.13303;2.071E-01 +11353.13034;2.071E-01 +11363.12453;2.070E-01 +11373.11561;2.070E-01 +11383.10358;2.070E-01 +11393.08842;2.069E-01 +11403.07013;2.069E-01 +11413.04872;2.069E-01 +11423.02418;2.068E-01 +11432.99651;2.068E-01 +11442.96571;2.068E-01 +11452.93177;2.068E-01 +11463.55877;2.067E-01 +11473.51834;2.067E-01 +11483.47476;2.067E-01 +11493.42803;2.066E-01 +11503.37815;2.066E-01 +11513.32512;2.066E-01 +11523.26893;2.066E-01 +11533.20958;2.065E-01 +11543.14707;2.065E-01 +11553.0814;2.065E-01 +11563.01256;2.064E-01 +11572.94055;2.064E-01 +11582.86536;2.064E-01 +11593.44833;2.064E-01 +11603.36658;2.063E-01 +11613.28165;2.063E-01 +11623.19353;2.063E-01 +11633.10223;2.063E-01 +11643.00774;2.062E-01 +11652.91006;2.062E-01 +11662.80918;2.062E-01 +11673.36472;2.061E-01 +11683.25723;2.061E-01 +11693.14654;2.061E-01 +11703.03264;2.061E-01 +11712.91554;2.060E-01 +11722.79522;2.060E-01 +11733.33001;2.060E-01 +11743.20304;2.060E-01 +11753.07286;2.059E-01 +11762.93946;2.059E-01 +11772.80283;2.059E-01 +11783.3202;2.058E-01 +11793.1769;2.058E-01 +11803.03036;2.058E-01 +11812.88059;2.058E-01 +11822.72758;2.057E-01 +11833.22747;2.057E-01 +11843.06776;2.057E-01 +11852.9048;2.057E-01 +11862.73859;2.056E-01 +11873.22439;2.056E-01 +11883.05145;2.056E-01 +11892.87526;2.056E-01 +11902.69581;2.055E-01 +11913.16747;2.055E-01 +11922.98127;2.055E-01 +11932.79181;2.055E-01 +11942.59907;2.054E-01 +11953.05654;2.054E-01 +11962.85704;2.054E-01 +11972.65425;2.054E-01 +11983.10099;2.053E-01 +11992.89142;2.053E-01 +12002.67856;2.053E-01 +12013.11455;2.053E-01 +12022.89488;2.052E-01 +12032.67192;2.052E-01 +12043.09713;2.052E-01 +12052.86735;2.052E-01 +12062.63426;2.051E-01 +12073.04866;2.051E-01 +12082.80874;2.051E-01 +12092.5655;2.051E-01 +12102.96906;2.050E-01 +12112.71897;2.050E-01 +12122.46556;2.050E-01 +12132.85826;2.050E-01 +12142.59798;2.050E-01 +12152.98334;2.049E-01 +12162.71618;2.049E-01 +12172.44568;2.049E-01 +12182.82013;2.049E-01 +12192.54273;2.048E-01 +12202.90982;2.048E-01 +12212.62551;2.048E-01 +12222.98523;2.048E-01 +12232.69399;2.047E-01 +12242.39941;2.047E-01 +12252.74815;2.047E-01 +12262.44662;2.047E-01 +12272.78796;2.046E-01 +12282.47948;2.046E-01 +12292.81339;2.046E-01 +12302.49796;2.046E-01 +12312.82444;2.046E-01 +12322.50203;2.045E-01 +12332.82106;2.045E-01 +12342.49167;2.045E-01 +12352.80325;2.045E-01 +12362.46686;2.044E-01 +12372.77097;2.044E-01 +12382.42757;2.044E-01 +12392.7242;2.044E-01 +12402.37378;2.044E-01 +12412.66292;2.043E-01 +12422.30547;2.043E-01 +12432.58711;2.043E-01 +12442.22262;2.043E-01 +12452.49673;2.043E-01 +12462.76697;2.042E-01 +12472.39178;2.042E-01 +12482.65448;2.042E-01 +12492.27222;2.042E-01 +12502.52738;2.041E-01 +12512.77863;2.041E-01 +12522.38563;2.041E-01 +12532.62932;2.041E-01 +12542.22922;2.041E-01 +12552.46533;2.040E-01 +12562.69753;2.040E-01 +12572.28665;2.040E-01 +12582.51125;2.040E-01 +12592.73192;2.040E-01 +12602.31024;2.039E-01 +12612.5233;2.039E-01 +12622.09447;2.039E-01 +12632.29991;2.039E-01 +12642.5014;2.039E-01 +12652.69895;2.038E-01 +12662.25557;2.038E-01 +12672.44547;2.038E-01 +12682.63142;2.038E-01 +12692.17715;2.038E-01 +12702.35544;2.037E-01 +12712.52975;2.037E-01 +12722.06458;2.037E-01 +12732.23121;2.037E-01 +12742.39387;2.037E-01 +12752.55256;2.036E-01 +12762.07271;2.036E-01 +12772.22369;2.036E-01 +12782.37068;2.036E-01 +12792.51369;2.036E-01 +12802.01913;2.035E-01 +12812.1544;2.035E-01 +12822.28568;2.035E-01 +12832.41295;2.035E-01 +12842.53622;2.035E-01 +12852.02315;2.034E-01 +12862.13866;2.034E-01 +12872.25016;2.034E-01 +12882.35764;2.034E-01 +12892.4611;2.034E-01 +12901.92944;2.033E-01 +12912.02511;2.033E-01 +12922.11675;2.033E-01 +12932.20436;2.033E-01 +12942.28794;2.033E-01 +12952.36747;2.032E-01 +12962.44297;2.032E-01 +12971.88508;2.032E-01 +12981.95274;2.032E-01 +12992.01635;2.032E-01 +13002.07591;2.032E-01 +13012.13141;2.031E-01 +13022.18286;2.031E-01 +13032.23024;2.031E-01 +13042.27356;2.031E-01 +13052.31281;2.031E-01 +13062.34799;2.030E-01 +13072.37909;2.030E-01 +13081.77955;2.030E-01 +13091.80275;2.030E-01 +13101.82186;2.030E-01 +13111.83689;2.030E-01 +13121.84783;2.029E-01 +13131.85468;2.029E-01 +13141.85743;2.029E-01 +13151.85608;2.029E-01 +13161.85063;2.029E-01 +13171.84107;2.029E-01 +13181.8274;2.028E-01 +13191.80963;2.028E-01 +13201.78774;2.028E-01 +13211.76172;2.028E-01 +13221.73159;2.028E-01 +13231.69734;2.028E-01 +13241.65895;2.027E-01 +13252.23865;2.027E-01 +13262.19174;2.027E-01 +13272.1407;2.027E-01 +13282.08552;2.027E-01 +13292.02619;2.027E-01 +13301.96272;2.026E-01 +13311.8951;2.026E-01 +13321.82333;2.026E-01 +13331.7474;2.026E-01 +13341.66731;2.026E-01 +13351.58306;2.026E-01 +13362.11398;2.025E-01 +13372.02114;2.025E-01 +13381.92413;2.025E-01 +13391.82294;2.025E-01 +13401.71758;2.025E-01 +13411.60803;2.025E-01 +13422.11206;2.024E-01 +13431.99388;2.024E-01 +13441.87151;2.024E-01 +13451.74495;2.024E-01 +13461.6142;2.024E-01 +13472.09567;2.024E-01 +13481.95625;2.024E-01 +13491.81263;2.023E-01 +13501.66479;2.023E-01 +13511.51275;2.023E-01 +13521.97158;2.023E-01 +13531.81084;2.023E-01 +13541.64588;2.023E-01 +13551.47669;2.022E-01 +13561.9173;2.022E-01 +13571.7394;2.022E-01 +13581.55725;2.022E-01 +13591.98409;2.022E-01 +13601.79321;2.022E-01 +13611.59808;2.022E-01 +13621.39871;2.021E-01 +13631.80722;2.021E-01 +13641.59909;2.021E-01 +13651.38669;2.021E-01 +13661.78136;2.021E-01 +13671.56018;2.021E-01 +13681.94551;2.021E-01 +13691.71553;2.020E-01 +13701.48128;2.020E-01 +13711.85271;2.020E-01 +13721.60964;2.020E-01 +13731.3623;2.020E-01 +13741.7198;2.020E-01 +13751.46361;2.020E-01 +13761.81172;2.019E-01 +13771.54668;2.019E-01 +13781.88538;2.019E-01 +13791.61148;2.019E-01 +13801.33328;2.019E-01 +13811.65798;2.019E-01 +13821.37089;2.019E-01 +13831.68615;2.018E-01 +13841.39017;2.018E-01 +13851.69596;2.018E-01 +13861.39108;2.018E-01 +13871.6874;2.018E-01 +13881.37359;2.018E-01 +13891.66043;2.018E-01 +13901.33769;2.018E-01 +13911.61502;2.017E-01 +13921.28334;2.017E-01 +13931.55116;2.017E-01 +13941.21051;2.017E-01 +13951.46881;2.017E-01 +13961.72219;2.017E-01 +13971.36795;2.017E-01 +13981.61179;2.016E-01 +13991.24855;2.016E-01 +14001.48283;2.016E-01 +14011.71217;2.016E-01 +14021.33529;2.016E-01 +14031.55505;2.016E-01 +14041.16914;2.016E-01 +14051.37931;2.016E-01 +14061.58453;2.015E-01 +14071.18493;2.015E-01 +14081.38053;2.015E-01 +14091.57118;2.015E-01 +14101.15785;2.015E-01 +14111.33886;2.015E-01 +14121.51491;2.015E-01 +14131.08782;2.015E-01 +14141.25421;2.014E-01 +14151.41561;2.014E-01 +14161.57204;2.014E-01 +14171.12647;2.014E-01 +14181.27321;2.014E-01 +14191.41496;2.014E-01 +14201.55171;2.014E-01 +14211.08762;2.014E-01 +14221.21466;2.014E-01 +14231.33669;2.013E-01 +14241.45371;2.013E-01 +14250.97103;2.013E-01 +14261.07831;2.013E-01 +14271.18057;2.013E-01 +14281.27781;2.013E-01 +14291.37001;2.013E-01 +14301.45718;2.013E-01 +14310.94639;2.013E-01 +14321.02379;2.012E-01 +14331.09614;2.012E-01 +14341.16344;2.012E-01 +14351.2257;2.012E-01 +14361.2829;2.012E-01 +14371.33504;2.012E-01 +14381.38213;2.012E-01 +14391.42415;2.012E-01 +14400.87083;2.012E-01 +14410.90301;2.011E-01 +14420.93011;2.011E-01 +14430.95214;2.011E-01 +14440.96908;2.011E-01 +14450.98094;2.011E-01 +14460.98771;2.011E-01 +14470.98939;2.011E-01 +14480.98597;2.011E-01 +14490.97745;2.011E-01 +14500.96383;2.010E-01 +14510.9451;2.010E-01 +14520.92127;2.010E-01 +14530.89232;2.010E-01 +14540.85825;2.010E-01 +14550.81906;2.010E-01 +14560.77475;2.010E-01 +14571.31048;2.010E-01 +14581.25561;2.010E-01 +14591.19561;2.010E-01 +14601.13046;2.009E-01 +14611.06018;2.009E-01 +14620.98475;2.009E-01 +14630.90417;2.009E-01 +14640.81844;2.009E-01 +14650.72755;2.009E-01 +14661.21393;2.009E-01 +14671.11242;2.009E-01 +14681.00574;2.009E-01 +14690.89389;2.009E-01 +14700.77687;2.008E-01 +14710.65467;2.008E-01 +14721.10787;2.008E-01 +14730.975;2.008E-01 +14740.83695;2.008E-01 +14750.6937;2.008E-01 +14761.12461;2.008E-01 +14770.97066;2.008E-01 +14780.81152;2.008E-01 +14790.64717;2.008E-01 +14801.05571;2.008E-01 +14810.88064;2.007E-01 +14820.70034;2.007E-01 +14831.09199;2.007E-01 +14840.90095;2.007E-01 +14850.70469;2.007E-01 +14861.07941;2.007E-01 +14870.87237;2.007E-01 +14880.6601;2.007E-01 +14891.01786;2.007E-01 +14900.7948;2.007E-01 +14910.56649;2.007E-01 +14920.90726;2.007E-01 +14930.66813;2.006E-01 +14940.99744;2.006E-01 +14950.74749;2.006E-01 +14960.49227;2.006E-01 +14970.80453;2.006E-01 +14980.53846;2.006E-01 +14990.83922;2.006E-01 +15000.56229;2.006E-01 +15010.85154;2.006E-01 +15020.56373;2.006E-01 +15030.84146;2.006E-01 +15040.54275;2.006E-01 +15050.80894;2.006E-01 +15060.49932;2.005E-01 +15070.75395;2.005E-01 +15080.43342;2.005E-01 +15090.67647;2.005E-01 +15100.91357;2.005E-01 +15110.57647;2.005E-01 +15120.80197;2.005E-01 +15130.45391;2.005E-01 +15140.6678;2.005E-01 +15150.87571;2.005E-01 +15160.51103;2.005E-01 +15170.70731;2.005E-01 +15180.33163;2.005E-01 +15190.51626;2.005E-01 +15200.69489;2.005E-01 +15210.30253;2.004E-01 +15220.4695;2.004E-01 +15230.63045;2.004E-01 +15240.78539;2.004E-01 +15250.37064;2.004E-01 +15260.51388;2.004E-01 +15270.65109;2.004E-01 +15280.78227;2.004E-01 +15290.34507;2.004E-01 +15300.46452;2.004E-01 +15310.57793;2.004E-01 +15320.6853;2.004E-01 +15330.22559;2.004E-01 +15340.32119;2.004E-01 +15350.41073;2.004E-01 +15360.49422;2.004E-01 +15370.57164;2.003E-01 +15380.64299;2.003E-01 +15390.70827;2.003E-01 +15400.20879;2.003E-01 +15410.26225;2.003E-01 +15420.30963;2.003E-01 +15430.35092;2.003E-01 +15440.38612;2.003E-01 +15450.41522;2.003E-01 +15460.43823;2.003E-01 +15470.45513;2.003E-01 +15480.46592;2.003E-01 +15490.4706;2.003E-01 +15500.46917;2.003E-01 +15510.46161;2.003E-01 +15520.44794;2.003E-01 +15530.42813;2.003E-01 +15540.4022;2.003E-01 +15550.37013;2.003E-01 +15560.33192;2.003E-01 +15570.28757;2.003E-01 +15580.23707;2.002E-01 +15590.18042;2.002E-01 +15600.11761;2.002E-01 +15610.04865;2.002E-01 +15620.52472;2.002E-01 +15630.44309;2.002E-01 +15640.35528;2.002E-01 +15650.2613;2.002E-01 +15660.16114;2.002E-01 +15670.0548;2.002E-01 +15680.4914;2.002E-01 +15690.37234;2.002E-01 +15700.24708;2.002E-01 +15710.11563;2.002E-01 +15719.97797;2.002E-01 +15730.38149;2.002E-01 +15740.23107;2.002E-01 +15750.07444;2.002E-01 +15759.91159;2.002E-01 +15770.2885;2.002E-01 +15780.11286;2.002E-01 +15789.93098;2.002E-01 +15800.2878;2.002E-01 +15810.09311;2.002E-01 +15819.89218;2.002E-01 +15830.22886;2.002E-01 +15840.01509;2.002E-01 +15850.33821;2.002E-01 +15860.11158;2.002E-01 +15869.87869;2.002E-01 +15880.18161;2.002E-01 +15889.93584;2.001E-01 +15900.22516;2.001E-01 +15909.96648;2.001E-01 +15920.24218;2.001E-01 +15929.97059;2.001E-01 +15940.23264;2.001E-01 +15949.94812;2.001E-01 +15960.19652;2.001E-01 +15969.89905;2.001E-01 +15980.13377;2.001E-01 +15989.82334;2.001E-01 +16000.04436;2.001E-01 diff --git a/examples/beam_combine_test.rs b/examples/beam_combine_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..eda3b8209a604d4fd199d3578f439c18d73ea273 --- /dev/null +++ b/examples/beam_combine_test.rs @@ -0,0 +1,56 @@ +use std::fs::File; +use std::io::Write; + +use opossum::{ + analyzer::AnalyzerEnergy, + error::OpossumError, + lightdata::{DataEnergy, LightData}, + nodes::{BeamSplitter, Detector, FilterType, IdealFilter, Source}, + spectrum::{create_he_ne_spectrum, create_nd_glass_spectrum, Spectrum}, + OpticScenery, +}; + +fn main() -> Result<(), OpossumError> { + let mut scenery = OpticScenery::new(); + scenery.set_description("beam combiner demo"); + + let i_s1 = scenery.add_element( + "Source 1", + Source::new(LightData::Energy(DataEnergy { + spectrum: create_he_ne_spectrum(1.0), + })), + ); + let i_s2 = scenery.add_element( + "Source 2", + Source::new(LightData::Energy(DataEnergy { + spectrum: create_nd_glass_spectrum(1.0), + })), + ); + let i_bs = scenery.add_element("Beam splitter", BeamSplitter::new(0.5)); + let filter_spectrum = Spectrum::from_csv("NE03B.csv")?; + let i_f = scenery.add_element( + "Filter", + IdealFilter::new(FilterType::Spectrum(filter_spectrum))?, + ); + let i_d1 = scenery.add_element("Detector 1", Detector::default()); + + scenery.connect_nodes(i_s1, "out1", i_bs, "input1")?; + scenery.connect_nodes(i_s2, "out1", i_bs, "input2")?; + scenery.connect_nodes(i_bs, "out1_trans1_refl2", i_f, "front")?; + scenery.connect_nodes(i_f, "rear", i_d1, "in1")?; + + let path = "beam_combiner.dot"; + let mut output = File::create(path).unwrap(); + write!(output, "{}", scenery.to_dot()).unwrap(); + + scenery.report(); + println!(""); + let mut analyzer = AnalyzerEnergy::new(&scenery); + print!("Analyze..."); + analyzer.analyze()?; + println!("Sucessful"); + println!(""); + scenery.report(); + + Ok(()) +} diff --git a/examples/source_detector_test.rs b/examples/filter_test.rs similarity index 73% rename from examples/source_detector_test.rs rename to examples/filter_test.rs index d5838c5f94df43d49dab174bf2e436e25a15bdc3..ade6386bc273307c0f94f2dc44b52a3ba1194a14 100644 --- a/examples/source_detector_test.rs +++ b/examples/filter_test.rs @@ -1,27 +1,31 @@ use std::fs::File; use std::io::Write; -use uom::si::{energy::joule, f64::Energy}; use opossum::{ analyzer::AnalyzerEnergy, error::OpossumError, lightdata::{DataEnergy, LightData}, - nodes::{BeamSplitter, Detector, IdealFilter, Source}, - optic_scenery::OpticScenery, + nodes::{BeamSplitter, Detector, FilterType, IdealFilter, Source}, + spectrum::{create_he_ne_spectrum, Spectrum}, + OpticScenery, }; fn main() -> Result<(), OpossumError> { let mut scenery = OpticScenery::new(); - scenery.set_description("src - detector demo".into()); + scenery.set_description("filter system demo"); let i_s = scenery.add_element( "Source", Source::new(LightData::Energy(DataEnergy { - energy: Energy::new::<joule>(1.0), + spectrum: create_he_ne_spectrum(1.0), })), ); let i_bs = scenery.add_element("Beam splitter", BeamSplitter::new(0.6)); - let i_f = scenery.add_element("Filter", IdealFilter::new(0.5)?); + let filter_spectrum = Spectrum::from_csv("NE03B.csv")?; + let i_f = scenery.add_element( + "Filter", + IdealFilter::new(FilterType::Spectrum(filter_spectrum))?, + ); let i_d1 = scenery.add_element("Detector 1", Detector::default()); let i_d2 = scenery.add_element("Detector 2", Detector::default()); diff --git a/examples/graph_port_test.rs b/examples/graph_port_test.rs index f25bf1c4adfa404548ca6a3679c82161549fb29c..d3b7a958e83e9d79c4cd417cf93449e27910e111 100644 --- a/examples/graph_port_test.rs +++ b/examples/graph_port_test.rs @@ -1,14 +1,14 @@ use opossum::error::OpossumError; -use opossum::nodes::{Dummy, BeamSplitter}; +use opossum::nodes::{BeamSplitter, Dummy}; use opossum::optic_node::OpticNode; -use opossum::optic_scenery::OpticScenery; +use opossum::OpticScenery; use std::fs::File; use std::io::Write; -fn main() -> Result <(), OpossumError> { +fn main() -> Result<(), OpossumError> { let mut scenery = OpticScenery::new(); - scenery.set_description("Fancy Graph with Ports".into()); + scenery.set_description("Fancy Graph with Ports"); let in1 = scenery.add_node(OpticNode::new("Input", Dummy)); let out1 = scenery.add_node(OpticNode::new("Output", Dummy)); @@ -16,7 +16,7 @@ fn main() -> Result <(), OpossumError> { let bs2 = scenery.add_node(OpticNode::new("Beamsplitter 2", BeamSplitter::default())); let m1 = scenery.add_node(OpticNode::new("Mirror 1", Dummy)); let m2 = scenery.add_node(OpticNode::new("Mirror 2", Dummy)); - + scenery.connect_nodes(in1, "rear", bs1, "input1")?; scenery.connect_nodes(bs1, "out1_trans1_refl2", m1, "front")?; scenery.connect_nodes(bs1, "out2_trans2_refl1", m2, "front")?; @@ -26,7 +26,6 @@ fn main() -> Result <(), OpossumError> { scenery.connect_nodes(bs2, "out1_trans1_refl2", out1, "front")?; scenery.connect_nodes(bs2, "out2_trans2_refl1", out1, "front")?; - let path = "graph_w_ports.dot"; let mut output = File::create(path).unwrap(); write!(output, "{}", scenery.to_dot()?).unwrap(); diff --git a/examples/lens_test.rs b/examples/lens_test.rs index f744c1b2a197e8821f897fd2b0b99ee6221ea0ff..a32de638931f1419a79f28a2f880f56baa80f2b0 100644 --- a/examples/lens_test.rs +++ b/examples/lens_test.rs @@ -1,23 +1,22 @@ -use opossum::{ - nodes::{RealLens, Source, Detector}, - optic_scenery::OpticScenery, error::OpossumError, -}; +use opossum::nodes::{RealLens, Source}; +use opossum::{nodes::Detector, OpticScenery}; use std::fs::File; use std::io::Write; +use opossum::error::OpossumError; + fn main() -> Result<(), OpossumError> { let mut scenery = OpticScenery::new(); scenery.set_description("Lens Ray-trace test".into()); let src = scenery.add_element("Source", Source::default()); let l1 = scenery.add_element("Lens 1", RealLens::default()); let l2 = scenery.add_element("Lens 2", RealLens::default()); - let det=scenery.add_element("Detector", Detector::default()); + let det = scenery.add_element("Detector", Detector::default()); scenery.connect_nodes(src, "out1", l1, "in1")?; scenery.connect_nodes(l1, "out1", l2, "in1")?; scenery.connect_nodes(l2, "out1", det, "in1")?; - let path = "lens_system.dot"; let mut output = File::create(path).unwrap(); write!(output, "{}", scenery.to_dot()?).unwrap(); diff --git a/examples/michaelson.rs b/examples/michaelson.rs index fdb1efbd5b9cbbf5c96767232502f46eead54543..19376b17a2aaac023dd91d56b40368cbffd9ce4b 100644 --- a/examples/michaelson.rs +++ b/examples/michaelson.rs @@ -1,32 +1,32 @@ use opossum::{ - nodes::{BeamSplitter, Dummy, NodeReference, Source, Detector}, - optic_scenery::OpticScenery, error::OpossumError, + error::OpossumError, + nodes::{BeamSplitter, Detector, Dummy, NodeReference, Source}, + OpticScenery, }; use std::fs::File; use std::io::Write; fn main() -> Result<(), OpossumError> { let mut scenery = OpticScenery::new(); - scenery.set_description("Michaelson interferomater".into()); + scenery.set_description("Michaelson interferomater"); let src = scenery.add_element("Source", Source::default()); let bs = scenery.add_element("Beamspliiter", BeamSplitter::default()); let sample = scenery.add_element("sample", Dummy); - let rf = NodeReference::new(scenery.node(sample)?); - let r_sample=scenery.add_node(rf); + let rf = NodeReference::from_node(scenery.node(sample)?); + let r_sample = scenery.add_node(rf); let m1 = scenery.add_element("Mirror", Dummy); let m2 = scenery.add_element("Mirror", Dummy); - let rf = NodeReference::new(scenery.node(bs)?); + let rf = NodeReference::from_node(scenery.node(bs)?); let r_bs = scenery.add_node(rf); - let det=scenery.add_element("Detector", Detector::default()); + let det = scenery.add_element("Detector", Detector::default()); scenery.connect_nodes(src, "out1", bs, "input1")?; - scenery - .connect_nodes(bs, "out1_trans1_refl2", sample, "front")?; + scenery.connect_nodes(bs, "out1_trans1_refl2", sample, "front")?; scenery.connect_nodes(sample, "rear", m1, "front")?; - scenery.connect_nodes(m1,"rear", r_sample, "front")?; + scenery.connect_nodes(m1, "rear", r_sample, "front")?; scenery.connect_nodes(r_sample, "rear", r_bs, "input1")?; - scenery.connect_nodes(bs,"out2_trans2_refl1", m2, "front")?; - scenery.connect_nodes(m2,"rear", r_bs,"input2")?; + scenery.connect_nodes(bs, "out2_trans2_refl1", m2, "front")?; + scenery.connect_nodes(m2, "rear", r_bs, "input2")?; scenery.connect_nodes(r_bs, "out1_trans1_refl2", det, "in1")?; let path = "michaelson.dot"; diff --git a/examples/opticscenery.rs b/examples/opticscenery.rs index e1a3f70c3dece0b20cb9f3fd5d4bfb6089ca2a84..8cd1a6f11f79eb61bb0798e6da3b8568877103e3 100644 --- a/examples/opticscenery.rs +++ b/examples/opticscenery.rs @@ -1,6 +1,6 @@ use opossum::error::OpossumError; use opossum::nodes::Dummy; -use opossum::optic_scenery::OpticScenery; +use opossum::OpticScenery; use std::fs::File; use std::io::Write; @@ -8,7 +8,7 @@ use std::io::Write; fn main() -> Result<(), OpossumError> { println!("opticscenery example"); let mut scenery = OpticScenery::new(); - scenery.set_description("OpticScenery demo".into()); + scenery.set_description("OpticScenery demo"); println!("default opticscenery: {:?}", scenery); println!("export to `dot` format: {}", scenery.to_dot()?); let node1 = scenery.add_element("my optic", Dummy); diff --git a/examples/pa_doublepass_graph.rs b/examples/pa_doublepass_graph.rs index 4df02fe82347ec88abe869ac293f564d926b24ed..b08a30bbf6db713797680e5b2650114cb9f6b120 100644 --- a/examples/pa_doublepass_graph.rs +++ b/examples/pa_doublepass_graph.rs @@ -1,20 +1,20 @@ use opossum::analyzer::AnalyzerEnergy; use opossum::error::OpossumError; use opossum::nodes::{Dummy, NodeReference}; -use opossum::optic_scenery::OpticScenery; +use opossum::OpticScenery; use std::fs::File; use std::io::Write; fn main() -> Result<(), OpossumError> { let mut scenery = OpticScenery::new(); - scenery.set_description("PreAmp Doublepass section".into()); + scenery.set_description("PreAmp Doublepass section"); //let n0 = scenery.add_element("LightSource", Source::default()); let n1 = scenery.add_element("TFP", Dummy); let n2 = scenery.add_element("19mm amp", Dummy); //let n3 = scenery.add_element("Faraday", Dummy); let n4 = scenery.add_element("0° mirror", Dummy); - let mut node = NodeReference::new(scenery.node(n1).unwrap()); + let mut node = NodeReference::from_node(scenery.node(n1).unwrap()); node.set_inverted(true); let n1r = scenery.add_node(node); @@ -22,7 +22,7 @@ fn main() -> Result<(), OpossumError> { // node.set_inverted(true); // let n3r = scenery.add_node(node); - let mut node = NodeReference::new(scenery.node(n2)?); + let mut node = NodeReference::from_node(scenery.node(n2)?); node.set_inverted(true); let n2r = scenery.add_node(node); diff --git a/examples/spectrum_test.rs b/examples/spectrum_test.rs index 0208300345a513bcc5b09739b8cb1685411e1380..944cb2f28c9c89902480a4e8e08954620bd27e5b 100644 --- a/examples/spectrum_test.rs +++ b/examples/spectrum_test.rs @@ -1,15 +1,45 @@ use opossum::error::OpossumError; -use opossum::spectrum::Spectrum; -use uom::si::energy::joule; -use uom::si::f64::Energy; +use opossum::spectrum::{create_visible_spectrum, Spectrum}; use uom::si::{f64::Length, length::nanometer}; fn main() -> Result<(), OpossumError> { let mut s = Spectrum::new( - Length::new::<nanometer>(400.0)..Length::new::<nanometer>(410.0), - Length::new::<nanometer>(1.0), + Length::new::<nanometer>(400.0)..Length::new::<nanometer>(450.0), + Length::new::<nanometer>(0.1), )?; - s.set_single_peak(Length::new::<nanometer>(409.0), Energy::new::<joule>(1.0))?; - println!("{}", s); + s.add_lorentzian_peak( + Length::new::<nanometer>(415.0), + Length::new::<nanometer>(3.2), + 2.0, + )?; + + let mut s2 = Spectrum::new( + Length::new::<nanometer>(400.0)..Length::new::<nanometer>(450.0), + Length::new::<nanometer>(2.1), + )?; + s2.add_lorentzian_peak( + Length::new::<nanometer>(430.0), + Length::new::<nanometer>(1.2), + 0.5, + )?; + s.add(&s2); + + let mut s3 = Spectrum::new( + Length::new::<nanometer>(400.0)..Length::new::<nanometer>(450.0), + Length::new::<nanometer>(0.05), + )?; + s3.add_lorentzian_peak( + Length::new::<nanometer>(420.0), + Length::new::<nanometer>(0.3), + 0.02, + )?; + s.sub(&s3); + s.to_plot("spectrum.svg"); + + let s4 = Spectrum::from_csv("NE03B.csv")?; + s4.to_plot("ne03b_raw.svg"); + let mut s5 = create_visible_spectrum(); + s5.resample(&s4); + s5.to_plot("ne03b.svg"); Ok(()) } diff --git a/examples/structtest.rs b/examples/structtest.rs deleted file mode 100644 index 37b9798b6febf8646d580b528a946f2129425015..0000000000000000000000000000000000000000 --- a/examples/structtest.rs +++ /dev/null @@ -1,58 +0,0 @@ -struct OpticDummy; - -struct OpticIdealLens { - focal_length: f64, -} -impl OpticIdealLens { - pub fn new(focal_length: f64) -> Self { - Self{focal_length} - } -} -trait Optical { - fn analyze(&self) { - println!("generic analyze"); - } -} -impl Optical for OpticDummy { - fn analyze(&self) { - println!("optic dummy analyze"); - } -} -impl Optical for OpticIdealLens { - fn analyze(&self) { - println!("ideal lens analyze. f={}",self.focal_length); - } -} -struct OpticNode<T: Optical> { - name: String, - node: T -} -impl <T: Optical> OpticNode<T> { - pub fn new(name: &str, t: T) -> Self { - Self{name: name.into(), node : t} - } - // pub fn node_mut(&mut self) -> &mut T { - // &mut self.node - // } - pub fn analyze(&mut self) { - print!("Analyze element {}: ",self.name); - self.node.analyze(); - } -} - -impl OpticNode<OpticIdealLens> { - pub fn set_focal_length(&mut self, f: f64) { - self.node.focal_length=f; - } -} -fn main() { - let mut node=OpticNode::new("Test1", OpticDummy); - node.analyze(); - - let mut node=OpticNode::new("Test2", OpticIdealLens::new(1.23)); - node.analyze(); - node.set_focal_length(3.45); - node.analyze(); - - // let g: DiGraph<OpticNode<>,()>=DiGraph::new(); // does not work since it needs a concrete type here.... -} \ No newline at end of file diff --git a/examples/structtest2.rs b/examples/structtest2.rs deleted file mode 100644 index 02ba3d07c10ae43bd06c5106af3e6cd309445e83..0000000000000000000000000000000000000000 --- a/examples/structtest2.rs +++ /dev/null @@ -1,60 +0,0 @@ -use petgraph::prelude::DiGraph; -trait Optical { - fn analyze(&self) { - println!("generic analyze"); - } -} - -struct OpticIdealLens { - focal_length: f64, -} - -impl OpticIdealLens { - pub fn new(focal_length: f64) -> Self { - Self{focal_length} - } -} - -impl Optical for OpticIdealLens { - fn analyze(&self) { - println!("ideal lens analyze: f={}",self.focal_length); - } -} -enum NodeType { - Dummy, - IdealLens(OpticIdealLens), - SimpleElement(f64) -} -impl NodeType { - fn analyze(&self) { - match self { - NodeType::Dummy => println!("dummy -> nothing to do here"), - NodeType::IdealLens(n) => n.analyze(), - _ => println!("not covered") - } - } -} -struct OpticNode { - name: String, - node: NodeType -} - -impl OpticNode { - fn new(name: &str, node_type: NodeType) -> Self { - Self{name: name.into(), node: node_type} - } - fn analyze(&self) { - print!("Analyze {}: ",self.name); - self.node.analyze(); - } -} -fn main() { - let node=OpticNode::new("Test1",NodeType::Dummy); - node.analyze(); - let node=OpticNode::new("Test2",NodeType::IdealLens(OpticIdealLens::new(1.23))); - node.analyze(); - let node=OpticNode::new("Test2",NodeType::SimpleElement(1.23)); - node.analyze(); - - let _p:DiGraph<OpticNode,()> = DiGraph::new(); -} \ No newline at end of file diff --git a/examples/trait_test.rs b/examples/trait_test.rs deleted file mode 100644 index cffc7d6d46a9d6344b6e483afb8efa6e77414c30..0000000000000000000000000000000000000000 --- a/examples/trait_test.rs +++ /dev/null @@ -1,55 +0,0 @@ -trait Analyzer {} - -struct AnalyzerEnergy{} -impl Analyzer for AnalyzerEnergy {} - -struct AnalyzerRay{} -impl Analyzer for AnalyzerRay {} - -trait Analyzable<T: Analyzer> { - fn analyze(&self, _analyzer: T) { - println!("Default Analyze"); - } -} - -trait Optical {} -struct Lens {} - -impl Analyzable<AnalyzerEnergy> for Lens { - fn analyze(&self, _analyzer: AnalyzerEnergy) { - println!("Lens Analyze Energy"); - } -} - -impl Analyzable<AnalyzerRay> for Lens { - fn analyze(&self, _analyzer: AnalyzerRay) { - println!("Lens Analyze Ray"); - } -} -impl Optical for Lens {} -struct Mirror {} - -impl Analyzable<AnalyzerEnergy> for Mirror { - fn analyze(&self, _analyzer: AnalyzerEnergy) { - println!("Mirror Analyze Energy"); - } -} - -impl Analyzable<AnalyzerRay> for Mirror { - fn analyze(&self, _analyzer: AnalyzerRay) { - println!("Mirror Analyze Ray"); - } -} - -impl Optical for Mirror {} -fn main() { - let lens= Lens{}; - let mirror= Mirror{}; - - let _comp: Vec<Box<dyn Optical>> = vec![Box::new(lens), Box::new(mirror)]; - - //comp[0].analyze(AnalyzerEnergy{}); - // lens.analyze(AnalyzerRay{}); - // mirror.analyze(AnalyzerEnergy{}); - // mirror.analyze(AnalyzerRay{}); -} \ No newline at end of file diff --git a/examples/trait_test2.rs b/examples/trait_test2.rs deleted file mode 100644 index f7e44ee627f05dd370bfe0c0eb19140a41a61694..0000000000000000000000000000000000000000 --- a/examples/trait_test2.rs +++ /dev/null @@ -1,71 +0,0 @@ -type Scenery = Vec<Box<dyn Optical>>; - -pub enum AnalyzerType { - Energy, - Ray, -} - -trait Analyzer { - fn analyze(&self, _scenery: &Scenery) { - println!("No implemented"); - } -} - -struct AnalyzerEnergy {} - -impl Analyzer for AnalyzerEnergy { - fn analyze(&self, scenery: &Scenery) { - for element in scenery.iter() { - element.analyze(AnalyzerType::Energy) - } - } -} - -struct AnalyzerRay {} - -impl Analyzer for AnalyzerRay { - fn analyze(&self, scenery: &Scenery) { - for element in scenery.iter() { - element.analyze(AnalyzerType::Ray) - } -} -} -trait Optical { - fn analyze(&self, _anatype: AnalyzerType) { - println!("Default"); - } -} -struct Lens {} - -impl Optical for Lens { - fn analyze(&self, anatype: AnalyzerType) { - print!("Lens: "); - match anatype { - AnalyzerType::Energy => println!("Energy"), - AnalyzerType::Ray => println!("Ray"), - } - } -} -struct Mirror {} - -impl Optical for Mirror { - fn analyze(&self, anatype: AnalyzerType) { - print!("Mirror: "); - match anatype { - AnalyzerType::Energy => println!("Energy"), - AnalyzerType::Ray => println!("Ray"), - } - } -} -fn main() { - let lens = Lens {}; - let mirror = Mirror {}; - - let comp: Scenery = vec![Box::new(lens), Box::new(mirror)]; - - let a1 = AnalyzerEnergy {}; - let a2 = AnalyzerRay {}; - - a1.analyze(&comp); - a2.analyze(&comp); -} diff --git a/examples/uopa_graph.rs b/examples/uopa_graph.rs index e2accabf8137a929abdfc54a395b6a924d0d09b9..b19f9ebaa91bd265f0d39960b4ab52c8eafd13b8 100644 --- a/examples/uopa_graph.rs +++ b/examples/uopa_graph.rs @@ -1,7 +1,7 @@ use opossum::analyzer::AnalyzerEnergy; use opossum::error::OpossumError; use opossum::nodes::{BeamSplitter, Dummy}; -use opossum::optic_scenery::OpticScenery; +use opossum::OpticScenery; use std::fs::File; use std::io::Write; @@ -10,7 +10,7 @@ fn main() -> Result<(), OpossumError> { println!("PHELIX uOPA opticscenery example"); let mut scenery = OpticScenery::new(); - scenery.set_description("PHELIX uOPA".into()); + scenery.set_description("PHELIX uOPA"); println!("default opticscenery: {:?}", scenery); println!("export to `dot` format: {}", scenery.to_dot()?); diff --git a/logo/opossum.svg b/logo/opossum.svg index 02da7346c8bddf412a3e7dce93ab155590ff38ad..139e27ec2a2149ccd5173a10223dcd5f17f5ff66 100644 --- a/logo/opossum.svg +++ b/logo/opossum.svg @@ -4,11 +4,11 @@ <svg version="1.1" id="svg870" - width="500.24539" - height="466.55075" - viewBox="0 0 500.24539 466.55075" - sodipodi:docname="Unbenannt.svg" - inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + width="504.50769" + height="467.74179" + viewBox="0 0 504.50769 467.74179" + sodipodi:docname="opossum.svg" + inkscape:version="1.2.2 (732a01da63, 2022-12-09)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -23,28 +23,67 @@ inkscape:pageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" - showgrid="false" + showgrid="true" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" - inkscape:zoom="1.5195312" - inkscape:cx="249.41902" - inkscape:cy="237.24422" - inkscape:window-width="2560" - inkscape:window-height="1376" - inkscape:window-x="0" - inkscape:window-y="27" + inkscape:zoom="1.1734241" + inkscape:cx="243.30504" + inkscape:cy="281.22824" + inkscape:window-width="1680" + inkscape:window-height="997" + inkscape:window-x="-8" + inkscape:window-y="-8" inkscape:window-maximized="1" - inkscape:current-layer="g876" /> + inkscape:current-layer="g876" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1"> + <inkscape:grid + type="xygrid" + id="grid239" + originx="2.2538492" + originy="2.7415204" /> + </sodipodi:namedview> <g inkscape:groupmode="layer" inkscape:label="Image" id="g876" - transform="translate(-134.15938,-146.9612)"> + transform="translate(-131.90553,-144.21967)"> <path style="fill:#000000" - d="m 375,612.64916 c -8.52221,-2.01715 -17.48157,-6.10366 -24.74452,-11.2864 C 343.29287,596.39435 322,576.39508 322,574.82389 c 0,-0.50308 2.78953,1.34242 6.19895,4.10111 4.6694,3.77819 7.55742,5.29782 11.70453,6.15876 7.65057,1.58825 15.2758,-0.12243 18.07626,-4.05531 2.68016,-3.76394 2.58081,-4.37747 -1.22974,-7.59418 -4.46563,-3.76969 -10.54056,-6.38606 -14.88871,-6.41231 -3.39085,-0.0205 -3.68544,-0.27105 -4.32473,-3.67876 -0.37731,-2.01124 -2.19892,-8.48214 -4.04803,-14.37979 -3.99088,-12.72873 -5.10752,-20.95095 -3.60373,-26.5356 4.15643,-15.43581 24.83726,-27.83497 48.52777,-29.09477 21.38609,-1.13725 43.34516,7.83069 51.5056,21.03455 3.28599,5.31686 4.3176,12.24935 3.17672,21.34799 -1.5868,12.65501 -5.2247,27.28442 -6.78482,27.28442 -3.14146,0 -14.66359,6.19866 -18.24369,9.81471 -4.09933,4.14051 -7.06638,9.03964 -7.06638,11.66785 0,0.87004 1.0125,1.98886 2.25,2.48628 2.06754,0.83105 2.08781,0.91048 0.25,0.97958 -2.58116,0.097 7.54179,5.02533 11.29897,5.50083 1.51243,0.19141 4.39416,-0.21727 6.40384,-0.90816 5.13266,-1.76453 16.98798,-10.96369 25.54719,-19.82339 3.9875,-4.1275 7.24989,-7.215 7.24977,-6.86112 -3.3e-4,0.90301 -26.05123,28.72999 -31.77709,33.94357 -5.87866,5.35271 -13.12895,9.54556 -19.98071,11.55488 -7.60617,2.23054 -20.66479,2.85089 -27.24197,1.29413 z m -89.5,-36.33272 c -14.2019,-0.37633 -60.83908,-3.16541 -81.4613,-4.87169 C 194.43499,570.65014 185.99749,570 185.2887,570 184.57992,570 184,569.04981 184,567.88846 c 0,-2.07193 0.23917,-2.09948 12.75,-1.46859 7.0125,0.35362 28.05,1.13489 46.75,1.73617 58.68751,1.88702 60.92095,2.04276 61.34799,4.27786 0.48855,2.55704 -2.48515,4.61708 -6.28912,4.35681 C 296.87649,576.6756 291,576.46218 285.5,576.31644 Z m 192.25,-0.86426 c -2.65903,-2.78494 1.46098,-3.73728 27.75,-6.41444 11.55,-1.17621 33.29023,-3.46587 48.31162,-5.08815 C 585.02532,560.57858 589,560.40102 589,562.37756 c 0,0.75765 -0.56595,1.84725 -1.25767,2.42133 -0.69172,0.57407 -10.92922,1.9952 -22.75,3.15805 -11.82078,1.16284 -31.57235,3.23166 -43.89237,4.59736 -26.40386,2.92693 -42.30718,3.99004 -43.34996,2.89788 z M 298.32655,565.42124 c -4.15256,-1.7855 -4.76373,-2.95158 -3.34375,-6.37971 1.75539,-4.23789 9.50036,-4.68121 13.99932,-0.80132 2.63572,2.27304 2.55585,4.37797 -0.23212,6.11777 -3.51758,2.1951 -6.97168,2.54744 -10.42345,1.06326 z M 498,564.72251 c 0,-3.66383 3.354,-5.76236 14.79173,-9.25492 6.43955,-1.96634 17.51685,-5.38732 24.61623,-7.60217 C 552.5186,543.15123 576.48051,537 579.73382,537 c 1.55986,0 2.26618,0.59227 2.26618,1.90026 0,1.47015 -1.18829,2.17753 -5.25,3.12528 -13.36167,3.11778 -56.26621,16.73976 -72.0651,22.88032 -3.76489,1.46329 -6.6849,1.38321 -6.6849,-0.18335 z m -8.61191,-12.15356 c -0.33114,-0.86292 -0.3774,-2.15442 -0.10281,-2.87 0.49373,-1.28664 28.80694,-13.80997 44.23595,-19.56618 33.63118,-12.54702 41.11767,-14.89387 42.64365,-13.36789 2.17693,2.17693 -0.1608,4.0688 -8.34277,6.75163 -8.1559,2.67428 -15.64716,5.63459 -44.82211,17.71229 -31.42161,13.00777 -32.79428,13.47089 -33.61191,11.34015 z m -189.47376,0.0121 c -0.57113,-0.57114 -35.82885,-5.26585 -49.41433,-6.57973 -6.05,-0.5851 -22.41573,-1.68798 -36.36828,-2.45083 -13.95255,-0.76285 -25.81584,-1.83456 -26.36287,-2.38159 -2.29279,-2.29279 0.62865,-3.1686 10.54017,-3.15979 27.89515,0.0248 63.25598,3.12607 96.94098,8.50205 2.13198,0.34026 2.75,0.04 2.75,-1.33611 0,-0.97625 0.496,-2.271 1.10223,-2.87723 1.45854,-1.45854 -2.13546,-6.3558 -7.28168,-9.92213 C 289.71925,530.91944 288,528.9792 288,528.064 c 0,-3.8636 4.91277,-5.35358 8.55887,-2.5958 1.86996,1.41436 6.44113,2.11137 6.44113,0.98214 0,-1.50815 -3.56443,-7.30945 -6.19658,-10.08526 -3.81905,-4.02749 -3.58864,-7.71775 0.51122,-8.18774 1.54804,-0.17746 3.85897,0.36162 5.13538,1.19795 2.83337,1.8565 3.54998,1.25894 3.54998,-2.96017 0,-4.78615 0.9241,-6.41512 3.63921,-6.41512 3.69115,0 4.1809,3.01282 2.24996,13.84114 -0.94733,5.31237 -2.00484,11.68386 -2.35004,14.15886 -0.34953,2.50613 -1.93726,6.39605 -3.58338,8.77924 -3.50435,5.07348 -3.54512,5.3716 -0.95575,6.98869 2.39305,1.49448 2.61566,5.47356 0.42857,7.66064 -1.46368,1.46368 -4.55659,2.11007 -5.51424,1.15243 z M 455.2,548.8 c -1.80656,-1.80656 -1.44395,-5.26925 0.76071,-7.26445 1.55712,-1.40917 2.8951,-1.6343 6.5,-1.09371 5.22054,0.78286 5.86779,2.59952 2.46671,6.9233 -2.25602,2.86807 -7.51803,3.64425 -9.72742,1.43486 z m 30.29447,-7.80895 c -1.27631,-2.0651 0.72409,-4.91704 4.8017,-6.84572 2.31211,-1.09361 12.75383,-6.10103 23.20383,-11.12761 31.02142,-14.92169 37,-17.02293 37,-13.00402 0,0.88279 -4.06181,3.51653 -10.00333,6.4863 -26.30308,13.14716 -52.11461,25.52968 -53.18765,25.51561 -0.65504,-0.009 -1.47159,-0.46964 -1.81455,-1.02456 z M 451.2313,534.75 c -3.61547,-5.36369 -6.23505,-11.00896 -6.21853,-13.40113 0.0185,-2.6771 4.84811,-17.4529 6.56953,-20.09887 1.89307,-2.90982 5.16987,-2.88804 6.50545,0.0432 0.99498,2.18374 0.39536,4.98568 -2.55711,11.94911 -0.69252,1.63333 -0.46629,1.64732 3.61531,0.22366 2.39473,-0.83528 4.80405,-2.1084 5.35405,-2.82915 0.55,-0.72076 10.9,-6.4018 23,-12.62454 12.1,-6.22274 32.97386,-17.11943 46.38635,-24.21488 14.15666,-7.48912 25.273,-12.77425 26.5,-12.59912 1.34184,0.19152 2.11365,1.02786 2.11365,2.29037 0,2.17726 -3.13196,4.11414 -33.5,20.7172 -24.44347,13.36393 -47.72446,24.94752 -59.5,29.60456 -14.18562,5.61019 -15.5,6.30581 -15.5,8.20327 0,1.42185 0.77672,1.61806 4.85419,1.22627 C 464.18403,522.72786 468,524.16623 468,526.68736 468,528.7838 457.79447,539 455.70021,539 c -0.88227,0 -2.89328,-1.9125 -4.46891,-4.25 z M 279,536.09221 c -2.35796,-1.33142 -46.56523,-12.58824 -67,-17.06069 -10.175,-2.22695 -19.5125,-4.48542 -20.75,-5.01882 -2.40457,-1.03645 -3.03406,-3.58468 -1.04897,-4.24638 1.82346,-0.60782 51.98935,9.66372 69.69162,14.26946 24.72621,6.43323 26.10735,6.90063 26.10735,8.83518 0,3.46633 -3.61764,5.13109 -7,3.22125 z m 199,-10.90478 c 0,-3.33204 6.73936,-7.24042 37.5,-21.74749 18.15,-8.55975 34.30664,-16.21045 35.90364,-17.00156 3.33744,-1.65327 7.20513,-1.88043 8.07422,-0.4742 0.89768,1.45247 -4.02851,4.2194 -39.97786,22.45462 -31.23681,15.8448 -41.5,19.99178 -41.5,16.76863 z m 15.03051,-96.17862 c -14.82608,-5.46029 -23.62749,-19.82323 -22.88081,-37.33894 0.60597,-14.21482 6.83902,-23.91364 20.08472,-31.25246 C 495.1142,357.71375 496.26931,357.5 506,357.5 c 9.5546,0 10.97594,0.25076 15.7859,2.78504 14.51043,7.64526 21.96315,20.14152 20.9964,35.20548 -0.86958,13.55 -9.77091,26.79328 -21.7823,32.40744 -7.54629,3.52716 -20.0592,4.02412 -27.96949,1.11085 z M 243.7626,427.5444 c -14.70048,-5.62855 -22.95874,-17.95364 -23.15958,-34.56467 -0.0931,-7.70254 0.20889,-9.15392 3.14691,-15.12258 3.85316,-7.82776 10.12926,-13.97228 18.07492,-17.69595 4.66537,-2.18638 7.10234,-2.65975 13.69618,-2.6604 7.05531,-7e-4 8.80108,0.39059 14.5,3.25002 8.16212,4.09535 11.31842,6.90993 15.45803,13.78442 8.38527,13.92513 6.16737,31.21405 -5.64876,44.03304 -6.0856,6.6021 -13.44423,9.72028 -23.8303,10.09799 -5.64939,0.20545 -9.7534,-0.17079 -12.2374,-1.12187 z m -73.99251,-69.77253 c -5.94301,-9.07019 -19.08994,-40.97754 -25.29746,-61.39646 -7.81214,-25.69715 -10.94269,-46.70372 -10.20962,-68.50849 0.99679,-29.64929 7.44289,-47.50095 22.73154,-62.95218 8.4165,-8.506 19.50898,-14.83948 29.69259,-16.95359 7.87892,-1.63566 24.88078,-0.68931 34.72609,1.9329 21.69182,5.77743 47.0272,23.17135 63.81051,43.8089 7.22649,8.88604 22.86865,32.33854 22.24352,33.35002 -0.26796,0.43358 -5.40256,2.42619 -11.41022,4.42802 -22.63614,7.54269 -38.59359,15.78223 -56.22242,29.03014 -22.16226,16.65474 -44.25942,45.38421 -54.8073,71.25733 -5.63158,13.81383 -12.00014,27.72248 -12.81862,27.99531 -0.51165,0.17055 -1.60903,-0.7258 -2.43861,-1.9919 z m 427.4334,-0.87251 c -0.93692,-1.70536 -3.10659,-5.48623 -4.82149,-8.40194 -1.7149,-2.91572 -6.81856,-12.41168 -11.34148,-21.10215 C 571.80617,309.65211 569.65503,306.27798 559.43105,293.5 547.06461,278.04438 530.04774,262.69407 511.5,250.26311 c -11.2636,-7.54903 -17.97198,-10.90117 -32.43407,-16.20712 -6.28874,-2.30725 -12.66748,-4.83282 -14.17498,-5.61238 l -2.7409,-1.41738 4.80069,-8.34285 C 485.37488,186.66514 515.26276,159.78696 542,151.19156 c 20.41336,-6.56242 36.55603,-5.54063 54.04542,3.42092 18.80884,9.63765 29.83542,24.88395 36.13083,49.95761 1.97942,7.88372 2.24046,11.44876 2.22812,30.42991 -0.0144,22.2324 -1.00569,31.24338 -5.4755,49.77549 -2.60735,10.81022 -7.75306,27.11698 -12.69313,40.22451 -3.8836,10.30437 -15.73041,35 -16.78996,35 -0.29634,0 -1.30537,-1.39529 -2.24229,-3.10064 z" - id="path1035" /> + d="m 167.51624,354.83902 c -5.94301,-9.07019 -19.08994,-40.97754 -25.29746,-61.39646 -7.81214,-25.69715 -10.94269,-46.70372 -10.20962,-68.50849 0.99679,-29.64929 7.44289,-47.50095 22.73154,-62.95218 8.4165,-8.506 19.50898,-14.83948 29.69259,-16.95359 7.87892,-1.63566 24.88078,-0.68931 34.72609,1.9329 21.69182,5.77743 47.0272,23.17135 63.81051,43.8089 7.22649,8.88604 22.86865,32.33854 22.24352,33.35002 -73.30788,14.90823 -118.40348,72.82551 -135.25856,132.7108 -0.51165,0.17055 -2.43861,-1.9919 -2.43861,-1.9919 z" + id="path568" + sodipodi:nodetypes="cssssssccc" /> + <path + id="path564" + style="fill:#000000" + d="m 189.77852,502.18385 c -0.14894,-0.002 -0.25357,0.006 -0.31055,0.0254 -1.98509,0.66169 -1.35574,3.20964 1.04883,4.24609 1.2375,0.5334 10.57501,2.79258 20.75,5.01953 20.43475,4.47245 64.64204,15.72913 67,17.06055 3.38236,1.90983 7,0.24367 7,-3.22266 0,-1.93455 -1.38124,-2.40076 -26.10742,-8.83398 -17.14906,-4.46181 -64.76385,-14.24069 -69.38086,-14.29493 z m 7.79883,28.26758 c -9.91151,-0.009 -12.83381,0.86737 -10.54102,3.16016 0.54703,0.54703 12.41075,1.61801 26.36328,2.38086 13.95254,0.76285 30.3172,1.86607 36.36719,2.45117 13.58547,1.31388 48.84489,6.00894 49.41602,6.58008 0.95764,0.95764 9.24414,4.38085 9.24414,-0.61914 0,-5 -10.00078,-5.00113 -13.91016,-5.45117 -3.90938,-0.45004 -69.04433,-8.47716 -96.93945,-8.50196 z m -11.0918,28.05274 c -3.12447,0.0362 -3.21875,0.53121 -3.21875,1.82617 0,1.16135 0.58028,2.11328 1.28906,2.11328 0.70879,0 9.1463,0.64875 18.75,1.44336 20.6222,1.70628 67.25905,4.49672 81.46094,4.87305 5.49999,0.14574 11.37817,0.35754 13.06055,0.47265 3.80396,0.26027 6.77761,-1.79843 6.28906,-4.35547 -0.42704,-2.23509 -2.66216,-2.39228 -61.34961,-4.27929 -18.69998,-0.60128 -39.73751,-1.38076 -46.75,-1.73438 -4.69156,-0.23658 -7.65657,-0.38106 -9.53125,-0.35937 z" /> + <path + style="fill:#000000" + d="m 384.15938,611.9612 c -12.6902,-0.043 -21.0961,-4.40156 -30,-10 -8.9039,-5.59844 -25,-20 -25,-25 15,5 25,15 35,5 5,-5 5,-5 -5,-10 -10,-5 -19.82509,-9.58142 -25,-20 -5.17491,-10.41859 -5,-20 0,-30 5,-10 25,-30 50,-30 25,0 45,20 50,30 5,10 5,19.99999 0,30 -5,10 -15,15 -25,20 -10,5 -10,5 -5,10 10,10 20,-10e-6 35,-5 0,5 -15,20 -25,25 -10,5 -17.3098,10.04302 -30,10 z" + id="path1035" + sodipodi:nodetypes="zzcczzzzzzzcczz" /> + <circle + style="fill:#000000;stroke:none;stroke-width:4.7811" + id="path293" + cx="254.15938" + cy="396.96121" + r="35" /> + <circle + style="fill:#000000;stroke:none;stroke-width:4.7811" + id="path293-5" + cx="504.15939" + cy="396.96121" + r="35" /> + <path + style="fill:#000000" + d="m 600.80252,354.83902 c 5.94301,-9.07019 19.08994,-40.97754 25.29746,-61.39646 7.81214,-25.69715 10.94269,-46.70372 10.20962,-68.50849 -0.99679,-29.64929 -7.44289,-47.50095 -22.73154,-62.95218 -8.4165,-8.506 -19.50898,-14.83948 -29.69259,-16.95359 -7.87892,-1.63566 -24.88078,-0.68931 -34.72609,1.9329 -21.69182,5.77743 -47.0272,23.17135 -63.81051,43.8089 -7.22649,8.88604 -22.86865,32.33854 -22.24352,33.35002 73.30788,14.90823 118.40348,72.82551 135.25856,132.7108 0.51165,0.17055 2.43861,-1.9919 2.43861,-1.9919 z" + id="path568-9" + sodipodi:nodetypes="cssssssccc" /> + <path + id="path564-0" + style="fill:#000000" + d="m 578.14766,502.18385 c 0.14894,-0.002 0.25357,0.006 0.31055,0.0254 1.98509,0.66169 1.35574,3.20964 -1.04883,4.24609 -1.2375,0.5334 -10.57501,2.79258 -20.75,5.01953 -20.43475,4.47245 -64.64204,15.72913 -67,17.06055 -3.38236,1.90983 -7,0.24367 -7,-3.22266 0,-1.93455 1.38124,-2.40076 26.10742,-8.83398 17.14906,-4.46181 64.76385,-14.24069 69.38086,-14.29493 z m -7.79883,28.26758 c 9.91151,-0.009 12.83381,0.86737 10.54102,3.16016 -0.54703,0.54703 -12.41075,1.61801 -26.36328,2.38086 -13.95254,0.76285 -30.3172,1.86607 -36.36719,2.45117 -13.58547,1.31388 -48.84489,6.00894 -49.41602,6.58008 -0.95764,0.95764 -9.24414,4.38085 -9.24414,-0.61914 0,-5 10.00078,-5.00113 13.91016,-5.45117 3.90938,-0.45004 69.04433,-8.47716 96.93945,-8.50196 z m 11.0918,28.05274 c 3.12447,0.0362 3.21875,0.53121 3.21875,1.82617 0,1.16135 -0.58028,2.11328 -1.28906,2.11328 -0.70879,0 -9.1463,0.64875 -18.75,1.44336 -20.6222,1.70628 -67.25905,4.49672 -81.46094,4.87305 -5.49999,0.14574 -11.37817,0.35754 -13.06055,0.47265 -3.80396,0.26027 -6.77761,-1.79843 -6.28906,-4.35547 0.42704,-2.23509 2.66216,-2.39228 61.34961,-4.27929 18.69998,-0.60128 39.73751,-1.38076 46.75,-1.73438 4.69156,-0.23658 7.65657,-0.38106 9.53125,-0.35937 z" /> </g> </svg> diff --git a/spectrum_test/spec_to_csv_test_01.csv b/spectrum_test/spec_to_csv_test_01.csv new file mode 100644 index 0000000000000000000000000000000000000000..66edb602c637bc6cd64090d64df243a60df5a4d0 --- /dev/null +++ b/spectrum_test/spec_to_csv_test_01.csv @@ -0,0 +1,6 @@ +500;5.00E+01 +501;4.981E+01 +502;4.982E+01 +503;4.984E+01 +504;4.996E+01 +505;5.010E+01 \ No newline at end of file diff --git a/spectrum_test/spec_to_csv_test_02.csv b/spectrum_test/spec_to_csv_test_02.csv new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/spectrum_test/spec_to_csv_test_02.csv @@ -0,0 +1 @@ + diff --git a/spectrum_test/spec_to_csv_test_03.csv b/spectrum_test/spec_to_csv_test_03.csv new file mode 100644 index 0000000000000000000000000000000000000000..5dbdcbdef1eb68ea811171b2ef7563d75d8781ae --- /dev/null +++ b/spectrum_test/spec_to_csv_test_03.csv @@ -0,0 +1,2 @@ +500;5.010E+01 +501 \ No newline at end of file diff --git a/spectrum_test/spec_to_csv_test_04.csv b/spectrum_test/spec_to_csv_test_04.csv new file mode 100644 index 0000000000000000000000000000000000000000..e4170c8734de48740f0944e003c217fd3945fc6c --- /dev/null +++ b/spectrum_test/spec_to_csv_test_04.csv @@ -0,0 +1,2 @@ +500;5.010E+01 +501;ABC \ No newline at end of file diff --git a/src/analyzer.rs b/src/analyzer.rs index fa150ec93b874b62731809f3d2ba56096673be3e..f4447897af65e7f08796f29c18dc1281ed44f757 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -1,4 +1,5 @@ -use crate::{optic_scenery::OpticScenery, error::OpossumError}; +//! Optical Analyzers +use crate::{error::OpossumError, optic_scenery::OpticScenery}; type Result<T> = std::result::Result<T, OpossumError>; #[derive(Debug)] @@ -13,10 +14,11 @@ impl AnalyzerEnergy { } } pub fn analyze(&mut self) -> Result<()> { - self.scene.analyze(&AnalyzerType::Energy) + self.scene.analyze(&AnalyzerType::Energy) } } pub enum AnalyzerType { - Energy -} \ No newline at end of file + Energy, + ParAxialRayTrace, +} diff --git a/src/error.rs b/src/error.rs index 96112aaeca1efcd32afc773930afeae94d4b54b1..9cc88ac688468eb4fd3b7a59f6e1582d26df3015 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,12 +1,20 @@ +//! Opossum specfic error structures use std::{error::Error, fmt::Display}; +/// Errors that can be returned by various OPOSSUM functions. #[derive(Debug, Clone)] pub enum OpossumError { + /// error while setting up an `OpticScenery` OpticScenery(String), + /// error while setting up an `OpticGroup`. The reasons are similar to [`OpossumError::OpticScenery`] OpticGroup(String), + /// (mostly internal) errors while dealing with optical ports. OpticPort(String), + /// mostly runtime errors occuring during the analysis of a scenery Analysis(String), + /// errors while handling optical spectra Spectrum(String), + /// errors not falling in one of the categories above Other(String), } diff --git a/src/lib.rs b/src/lib.rs index ed4f674f15b9b0ac701b381ee6b3629dd78afe0e..d8828a60effdcf9781b5ec07635ac280ad060870 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,12 @@ -//! This is the documentation for the OPOSSUM software package. OPOSSUM stands for OPen-source Optics Simulation Software and Unified Modeller. -//! -/// The basic structure containing the entire optical model -pub mod optic_scenery; +//! This is the documentation for the **OPOSSUM** software package. **OPOSSUM** stands for +//! **Op**en-source **O**ptics **S**imulation **S**oftware and **U**nified **M**odeller. +//! +mod light; +pub mod lightdata; /// The basic structure representing an optical element pub mod optic_node; -pub mod light; -pub mod lightdata; +/// The basic structure containing the entire optical model +mod optic_scenery; pub mod optic_ports; @@ -15,4 +16,6 @@ pub mod analyzer; pub mod error; -pub mod spectrum; \ No newline at end of file +pub mod spectrum; + +pub use optic_scenery::OpticScenery; diff --git a/src/light.rs b/src/light.rs index c5b35cc9cd2f045449f2ccf0a29e98fb8c9ce9f1..7bc5dc865ba05963d0a9f9686753c84b48c4119e 100644 --- a/src/light.rs +++ b/src/light.rs @@ -1,10 +1,15 @@ +//! Data structure for the graph edges. +//! +//! [`Light`] represents the information / data flowing from one node to another node. It contains information about +//! the respective source an target port names this edge connects as well as the actual light information (stored as +//! [`LightData`]). use crate::lightdata::LightData; #[derive(Debug, Clone)] pub struct Light { src_port: String, target_port: String, - data: Option<LightData> + data: Option<LightData>, } impl Light { @@ -12,7 +17,7 @@ impl Light { Self { src_port: src_port.into(), target_port: target_port.into(), - data: None + data: None, } } pub fn src_port(&self) -> &str { @@ -21,12 +26,6 @@ impl Light { pub fn target_port(&self) -> &str { self.target_port.as_ref() } - pub fn set_src_port(&mut self, src_port: String) { - self.src_port = src_port; - } - pub fn set_target_port(&mut self, target_port: String) { - self.target_port = target_port; - } pub fn data(&self) -> Option<&LightData> { self.data.as_ref() } @@ -43,7 +42,7 @@ mod test { let light = Light::new("test1", "test2"); assert_eq!(light.src_port, "test1"); assert_eq!(light.target_port, "test2"); - assert_eq!(light.data, None); + assert!(light.data.is_none()); } #[test] fn src_port() { @@ -55,16 +54,4 @@ mod test { let light = Light::new("test1", "test2"); assert_eq!(light.target_port(), "test2"); } - #[test] - fn set_src_port() { - let mut light = Light::new("test1", "test2"); - light.set_src_port("test3".into()); - assert_eq!(light.src_port, "test3"); - } - #[test] - fn set_target_port() { - let mut light = Light::new("test1", "test2"); - light.set_target_port("test3".into()); - assert_eq!(light.target_port, "test3"); - } } diff --git a/src/lightdata.rs b/src/lightdata.rs index b2ebfa4ad57b89471bc9d25c21c4417a5a9a0fd3..e514311ce6cae225e43d739ec3ec17fe7ee61738 100644 --- a/src/lightdata.rs +++ b/src/lightdata.rs @@ -1,32 +1,54 @@ +//! Data structures containing the light information flowing between [`OpticNodes`](crate::optic_node::OpticNode). use std::fmt::Display; -use uom::si::{f64::Energy, energy::joule}; use uom::fmt::DisplayStyle::Abbreviation; +use uom::si::{energy::joule, f64::Energy}; -#[derive(Debug, PartialEq, Clone)] +use crate::spectrum::Spectrum; + +/// Data structure defining the light properties. The actuals data type used depends on the +/// [`AnalyzerType`](crate::analyzer::AnalyzerType). For example, an energy analysis ([`LightData::Energy`]) only +/// contains a [`Spectrum`] information, while a geometric analysis ([`LightData::Geometric]) constains a set of optical +/// ray data. +#[derive(Debug, Clone)] pub enum LightData { + /// data type used for energy analysis. Energy(DataEnergy), + /// data type used for geometric optics analysis (ray tracing) Geometric(DataGeometric), + /// placeholder value for future Fourier optics analysis, nothing implementd yet. Fourier, } - +impl LightData { + pub fn export(&self, file_name: &str) { + match self { + LightData::Energy(d) => { + d.spectrum.to_plot(file_name); + } + _ => println!("no export function defined for this type of LightData"), + } + } +} impl Display for LightData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LightData::Energy(e) => { let ef = Energy::format_args(joule, Abbreviation); - write!(f, "Energy: {}", ef.with(e.energy)) - } , + write!( + f, + "Energy: {}", + ef.with(Energy::new::<joule>(e.spectrum.total_energy())) + ) + } _ => write!(f, "No display defined for this type of LightData"), } } } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct DataEnergy { - pub energy: Energy, + pub spectrum: Spectrum, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct DataGeometric { - ray: i32, + _ray: i32, } diff --git a/src/nodes/node_beam_splitter.rs b/src/nodes/beam_splitter.rs similarity index 51% rename from src/nodes/node_beam_splitter.rs rename to src/nodes/beam_splitter.rs index a4095aa0601211e938fa67d561e4848d2d385039..d1c6a4b2390a0b8fe51b80f99e0f481fa5bafe1f 100644 --- a/src/nodes/node_beam_splitter.rs +++ b/src/nodes/beam_splitter.rs @@ -1,18 +1,26 @@ use std::collections::HashMap; -use uom::{si::f64::Energy, num_traits::Zero}; use crate::{ analyzer::AnalyzerType, error::OpossumError, - lightdata::{LightData, DataEnergy}, + lightdata::{DataEnergy, LightData}, optic_node::{Dottable, LightResult, Optical}, optic_ports::OpticPorts, + spectrum::{merge_spectra, Spectrum}, }; type Result<T> = std::result::Result<T, OpossumError>; #[derive(Debug)] /// An ideal beamsplitter node with a given splitting ratio. +/// +/// ## Optical Ports +/// - Inputs +/// - `input1` +/// - `input2` +/// - Outputs +/// - `out1_trans1_refl2` +/// - `out2_trans2_refl1` pub struct BeamSplitter { ratio: f64, } @@ -36,30 +44,54 @@ impl BeamSplitter { let in1 = incoming_data.get("input1"); let in2 = incoming_data.get("input2"); - let mut in1_energy = Energy::zero(); - let mut in2_energy = Energy::zero(); + let mut out1_1_spectrum: Option<Spectrum> = None; + let mut out1_2_spectrum: Option<Spectrum> = None; + let mut out2_1_spectrum: Option<Spectrum> = None; + let mut out2_2_spectrum: Option<Spectrum> = None; if let Some(Some(in1)) = in1 { match in1 { - LightData::Energy(e) => in1_energy = e.energy, - _ => return Err(OpossumError::Analysis("expected energy value".into())), + LightData::Energy(e) => { + let mut s = e.spectrum.clone(); + s.scale_vertical(self.ratio).unwrap(); + out1_1_spectrum = Some(s); + let mut s = e.spectrum.clone(); + s.scale_vertical(1.0 - self.ratio).unwrap(); + out1_2_spectrum = Some(s); + } + _ => return Err(OpossumError::Analysis("expected DataEnergy value".into())), } } if let Some(Some(in2)) = in2 { match in2 { - LightData::Energy(e) => in2_energy = e.energy, - _ => return Err(OpossumError::Analysis("expected energy value".into())), + LightData::Energy(e) => { + let mut s = e.spectrum.clone(); + s.scale_vertical(self.ratio).unwrap(); + out2_1_spectrum = Some(s); + let mut s = e.spectrum.clone(); + s.scale_vertical(1.0 - self.ratio).unwrap(); + out2_2_spectrum = Some(s); + } + _ => return Err(OpossumError::Analysis("expected DataEnergy value".into())), } } - let out1_energy = Some(LightData::Energy(DataEnergy { - energy: in1_energy * self.ratio + in2_energy * (1.0 - self.ratio), - })); - let out2_energy = Some(LightData::Energy(DataEnergy { - energy: in1_energy * (1.0 - self.ratio) + in2_energy * self.ratio, - })); + let out1_spec = merge_spectra(out1_1_spectrum, out2_2_spectrum); + let out2_spec = merge_spectra(out1_2_spectrum, out2_1_spectrum); + let mut out1_data: Option<LightData> = None; + let mut out2_data: Option<LightData> = None; + if let Some(out1_spec) = out1_spec { + out1_data = Some(LightData::Energy(DataEnergy { + spectrum: out1_spec, + })) + } + if let Some(out2_spec) = out2_spec { + out2_data = Some(LightData::Energy(DataEnergy { + spectrum: out2_spec, + })) + } Ok(HashMap::from([ - ("out1_trans1_refl2".into(), out1_energy), - ("out2_trans2_refl1".into(), out2_energy), + ("out1_trans1_refl2".into(), out1_data), + ("out2_trans2_refl1".into(), out2_data), ])) } } @@ -90,6 +122,9 @@ impl Optical for BeamSplitter { ) -> Result<LightResult> { match analyzer_type { AnalyzerType::Energy => self.analyze_energy(incoming_data), + _ => Err(OpossumError::Analysis( + "analysis type not yet implemented".into(), + )), } } } diff --git a/src/nodes/node_detector.rs b/src/nodes/detector.rs similarity index 73% rename from src/nodes/node_detector.rs rename to src/nodes/detector.rs index 7235b959c9ef531f187ef6f82752172482281447..96878ce328ea9eb739e14db1633bb5460089f0f9 100644 --- a/src/nodes/node_detector.rs +++ b/src/nodes/detector.rs @@ -9,11 +9,18 @@ use std::fmt::Debug; type Result<T> = std::result::Result<T, OpossumError>; #[derive(Default)] -/// This node rerpresents an universal detector. Any [`LightData`] coming in will be stored internally for later display / export. So far it only has one input (in1). +/// This node represents an universal detector. +/// +/// Any [`LightData`] coming in will be stored internally for later display / export. So far it only has one input (in1). +/// +/// ## Optical Ports +/// - Inputs +/// - `in1` +/// - Outputs +/// - none pub struct Detector { light_data: Option<LightData>, } - impl Optical for Detector { fn node_type(&self) -> &str { "light sink: detector" @@ -37,12 +44,17 @@ impl Optical for Detector { } Ok(LightResult::default()) } + fn export_data(&self, file_name: &str) { + if let Some(data) = &self.light_data { + data.export(file_name) + } + } } impl Debug for Detector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.light_data { - Some(data) => write!(f,"{}",data), + Some(data) => write!(f, "{}", data), None => write!(f, "no data"), } } diff --git a/src/nodes/dummy.rs b/src/nodes/dummy.rs new file mode 100644 index 0000000000000000000000000000000000000000..ef52210dc30c6f1236a2422752d3379342764b5e --- /dev/null +++ b/src/nodes/dummy.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; + +use crate::analyzer::AnalyzerType; +use crate::error::OpossumError; +use crate::optic_node::{Dottable, LightResult, Optical}; +use crate::optic_ports::OpticPorts; + +type Result<T> = std::result::Result<T, OpossumError>; + +#[derive(Debug)] +/// A fake / dummy component without any optical functionality. +/// +/// Any [`LightResult`] is directly forwarded without any modification. It is mainly used for +/// development and debugging purposes. +/// +/// ## Optical Ports +/// - Inputs +/// - `front` +/// - Outputs +/// - `rear` +pub struct Dummy; + +impl Optical for Dummy { + /// Returns "dummy" as node type. + fn node_type(&self) -> &str { + "dummy" + } + fn ports(&self) -> OpticPorts { + let mut ports = OpticPorts::new(); + ports.add_input("front").unwrap(); + ports.add_output("rear").unwrap(); + ports + } + + fn analyze( + &mut self, + incoming_data: LightResult, + _analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { + if let Some(data) = incoming_data.get("front") { + Ok(HashMap::from([("rear".into(), data.clone())])) + } else { + Ok(HashMap::from([("rear".into(), None)])) + } + } +} + +impl Dottable for Dummy {} diff --git a/src/nodes/group.rs b/src/nodes/group.rs new file mode 100644 index 0000000000000000000000000000000000000000..02e7777ffc9b0d39b2f6204f72806cce61af0d3f --- /dev/null +++ b/src/nodes/group.rs @@ -0,0 +1,616 @@ +#![warn(missing_docs)] +use crate::analyzer::AnalyzerType; +use crate::error::OpossumError; +use crate::light::Light; +use crate::lightdata::LightData; +use crate::optic_node::{Dottable, LightResult}; +use crate::{ + optic_node::{OpticNode, Optical}, + optic_ports::OpticPorts, +}; +use petgraph::prelude::{DiGraph, EdgeIndex, NodeIndex}; +use petgraph::visit::EdgeRef; +use petgraph::{algo::*, Direction}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +type Result<T> = std::result::Result<T, OpossumError>; + +#[derive(Default, Debug, Clone)] +/// A node that represents a group of other [`OpticNode`]s arranges in a subgraph. +/// +/// All unconnected input and output ports of this subgraph could be used as ports of +/// this [`NodeGroup`]. For this, port mapping is neccessary (see below). +/// +/// ## Optical Ports +/// - Inputs +/// - defined by [`map_input_port`](NodeGroup::map_input_port()) function. +/// - Outputs +/// - defined by [`map_output_port`](NodeGroup::map_output_port()) function. +pub struct NodeGroup { + g: DiGraph<Rc<RefCell<OpticNode>>, Light>, + input_port_map: HashMap<String, (NodeIndex, String)>, + output_port_map: HashMap<String, (NodeIndex, String)>, +} + +impl NodeGroup { + /// Creates a new (empty) [`NodeGroup`]. + pub fn new() -> Self { + Self::default() + } + /// Add a given [`OpticNode`] to the (sub-)graph of this [`NodeGroup`]. + /// + /// This command just adds an [`OpticNode`] but does not connect it to existing nodes in the (sub-)graph. The given node is + /// consumed (owned) by the [`NodeGroup`]. + pub fn add_node(&mut self, node: OpticNode) -> NodeIndex { + self.g.add_node(Rc::new(RefCell::new(node))) + } + /// Connect (already existing) nodes denoted by the respective `NodeIndex`. + /// + /// Both node indices must exist. Otherwise an [`OpossumError::OpticScenery`] is returned. In addition, connections are + /// rejected and an [`OpossumError::OpticScenery`] is returned, if the graph would form a cycle (loop in the graph). **Note**: + /// The connection of two internal nodes might affect external port mappings (see [`map_input_port`](NodeGroup::map_input_port()) + /// & [`map_output_port`](NodeGroup::map_output_port()) functions). In this case no longer valid mappings will be deleted. + pub fn connect_nodes( + &mut self, + src_node: NodeIndex, + src_port: &str, + target_node: NodeIndex, + target_port: &str, + ) -> Result<EdgeIndex> { + if let Some(source) = self.g.node_weight(src_node) { + if !source.borrow().ports().outputs().contains(&src_port.into()) { + return Err(OpossumError::OpticScenery(format!( + "source node {} does not have a port {}", + source.borrow().name(), + src_port + ))); + } + } else { + return Err(OpossumError::OpticScenery( + "source node with given index does not exist".into(), + )); + } + if let Some(target) = self.g.node_weight(target_node) { + if !target + .borrow() + .ports() + .inputs() + .contains(&target_port.into()) + { + return Err(OpossumError::OpticScenery(format!( + "target node {} does not have a port {}", + target.borrow().name(), + target_port + ))); + } + } else { + return Err(OpossumError::OpticScenery( + "target node with given index does not exist".into(), + )); + } + if self.src_node_port_exists(src_node, src_port) { + return Err(OpossumError::OpticScenery(format!( + "src node with given port {} is already connected", + src_port + ))); + } + if self.target_node_port_exists(src_node, src_port) { + return Err(OpossumError::OpticScenery(format!( + "target node with given port {} is already connected", + target_port + ))); + } + let edge_index = self + .g + .add_edge(src_node, target_node, Light::new(src_port, target_port)); + if is_cyclic_directed(&self.g) { + self.g.remove_edge(edge_index); + return Err(OpossumError::OpticScenery( + "connecting the given nodes would form a loop".into(), + )); + } + let in_map = self.input_port_map.clone(); + let invalid_mapping = in_map + .iter() + .find(|m| m.1 .0 == target_node && m.1 .1 == target_port); + if let Some(input) = invalid_mapping { + self.input_port_map.remove(input.0); + } + let out_map = self.output_port_map.clone(); + let invalid_mapping = out_map + .iter() + .find(|m| m.1 .0 == src_node && m.1 .1 == src_port); + if let Some(input) = invalid_mapping { + self.output_port_map.remove(input.0); + } + Ok(edge_index) + } + fn src_node_port_exists(&self, src_node: NodeIndex, src_port: &str) -> bool { + self.g + .edges_directed(src_node, petgraph::Direction::Outgoing) + .any(|e| e.weight().src_port() == src_port) + } + fn target_node_port_exists(&self, target_node: NodeIndex, target_port: &str) -> bool { + self.g + .edges_directed(target_node, petgraph::Direction::Incoming) + .any(|e| e.weight().target_port() == target_port) + } + fn input_nodes(&self) -> Vec<NodeIndex> { + let mut input_nodes: Vec<NodeIndex> = Vec::default(); + for node_idx in self.g.node_indices() { + let incoming_edges = self.g.edges_directed(node_idx, Direction::Incoming).count(); + let input_ports = self + .g + .node_weight(node_idx) + .unwrap() + .borrow() + .ports() + .inputs() + .len(); + if input_ports != incoming_edges { + input_nodes.push(node_idx); + } + } + input_nodes + } + fn output_nodes(&self) -> Vec<NodeIndex> { + let mut output_nodes: Vec<NodeIndex> = Vec::default(); + for node_idx in self.g.node_indices() { + let outgoing_edges = self.g.edges_directed(node_idx, Direction::Outgoing).count(); + let output_ports = self + .g + .node_weight(node_idx) + .unwrap() + .borrow() + .ports() + .outputs() + .len(); + if output_ports != outgoing_edges { + output_nodes.push(node_idx); + } + } + output_nodes + } + /// Map an input port of an internal node to an external port of the group. + /// + /// In oder to use a [`NodeGroup`] from the outside, internal nodes / ports must be mapped to be visible. The + /// corresponding [`ports`](NodeGroup::ports()) function only returns ports that have been mapped before. + /// # Errors + /// + /// This function will return an error if + /// - an external input port name has already been assigned. + /// - the `input_node` / `internal_name` does not exist. + /// - the specified `input_node` is not an input node of the group (i.e. fully connected to other internal nodes). + /// - the `input_node` has an input port with the specified `internal_name` but is already internally connected. + pub fn map_input_port( + &mut self, + input_node: NodeIndex, + internal_name: &str, + external_name: &str, + ) -> Result<()> { + if self.input_port_map.contains_key(external_name) { + return Err(OpossumError::OpticGroup( + "external input port name already assigned".into(), + )); + } + if let Some(node) = self.g.node_weight(input_node) { + if !node + .borrow() + .ports() + .inputs() + .contains(&(internal_name.to_string())) + { + return Err(OpossumError::OpticGroup( + "internal input port name not found".into(), + )); + } + } else { + return Err(OpossumError::OpticGroup( + "internal node index not found".into(), + )); + } + if !self.input_nodes().contains(&input_node) { + return Err(OpossumError::OpticGroup( + "node to be mapped is not an input node of the group".into(), + )); + } + let incoming_edge_connected = self + .g + .edges_directed(input_node, Direction::Incoming) + .map(|e| e.weight().target_port()) + .any(|p| p == internal_name); + if incoming_edge_connected { + return Err(OpossumError::OpticGroup( + "port of input node is already internally connected".into(), + )); + } + self.input_port_map.insert( + external_name.to_string(), + (input_node, internal_name.to_string()), + ); + Ok(()) + } + /// Map an output port of an internal node to an external port of the group. + /// + /// In oder to use a [`NodeGroup`] from the outside, internal nodes / ports must be mapped to be visible. The + /// corresponding [`ports`](NodeGroup::ports()) function only returns ports that have been mapped before. + /// # Errors + /// + /// This function will return an error if + /// - an external output port name has already been assigned. + /// - the `output_node` / `internal_name` does not exist. + /// - the specified `output_node` is not an output node of the group (i.e. fully connected to other internal nodes). + /// - the `output_node` has an output port with the specified `internal_name` but is already internally connected. + pub fn map_output_port( + &mut self, + output_node: NodeIndex, + internal_name: &str, + external_name: &str, + ) -> Result<()> { + if self.output_port_map.contains_key(external_name) { + return Err(OpossumError::OpticGroup( + "external output port name already assigned".into(), + )); + } + if let Some(node) = self.g.node_weight(output_node) { + if !node + .borrow() + .ports() + .outputs() + .contains(&(internal_name.to_string())) + { + return Err(OpossumError::OpticGroup( + "internal output port name not found".into(), + )); + } + } else { + return Err(OpossumError::OpticGroup( + "internal node index not found".into(), + )); + } + if !self.output_nodes().contains(&output_node) { + return Err(OpossumError::OpticGroup( + "node to be mapped is not an output node of the group".into(), + )); + } + let outgoing_edge_connected = self + .g + .edges_directed(output_node, Direction::Outgoing) + .map(|e| e.weight().src_port()) + .any(|p| p == internal_name); + if outgoing_edge_connected { + return Err(OpossumError::OpticGroup( + "port of output node is already internally connected".into(), + )); + } + self.output_port_map.insert( + external_name.to_string(), + (output_node, internal_name.to_string()), + ); + Ok(()) + } + fn incoming_edges(&self, idx: NodeIndex) -> LightResult { + let edges = self.g.edges_directed(idx, Direction::Incoming); + edges + .into_iter() + .map(|e| { + ( + e.weight().target_port().to_owned(), + e.weight().data().cloned(), + ) + }) + .collect::<HashMap<String, Option<LightData>>>() + } + fn set_outgoing_edge_data(&mut self, idx: NodeIndex, port: String, data: Option<LightData>) { + let edges = self.g.edges_directed(idx, Direction::Outgoing); + let edge_ref = edges + .into_iter() + .filter(|idx| idx.weight().src_port() == port) + .last(); + if let Some(edge_ref) = edge_ref { + let edge_idx = edge_ref.id(); + let light = self.g.edge_weight_mut(edge_idx); + if let Some(light) = light { + light.set_data(data); + } + } // else outgoing edge not connected -> data dropped + } + fn analyze_group( + &mut self, + incoming_data: LightResult, + analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { + let g_clone = self.g.clone(); + let mut group_srcs = g_clone.externals(Direction::Incoming); + let mut light_result = LightResult::default(); + let sorted = toposort(&self.g, None).unwrap(); + for idx in sorted { + // Check if node is group src node + let incoming_edges = if group_srcs.any(|gs| gs == idx) { + // get from incoming_data + let assigned_ports = self.input_port_map.iter().filter(|p| p.1 .0 == idx); + let mut incoming = LightResult::default(); + for port in assigned_ports { + incoming.insert( + port.1 .1.to_owned(), + incoming_data.get(port.0).unwrap().clone(), + ); + } + incoming + } else { + self.incoming_edges(idx) + }; + let node = self.g.node_weight(idx).unwrap(); + let outgoing_edges = node.borrow_mut().analyze(incoming_edges, analyzer_type)?; + let mut group_sinks = self.g.externals(Direction::Outgoing); + // Check if node is group sink node + if group_sinks.any(|gs| gs == idx) { + let assigned_ports = self.output_port_map.iter().filter(|p| p.1 .0 == idx); + for port in assigned_ports { + light_result.insert( + port.0.to_owned(), + outgoing_edges.get(&port.1 .1).unwrap().clone(), + ); + } + } else { + for outgoing_edge in outgoing_edges { + self.set_outgoing_edge_data(idx, outgoing_edge.0, outgoing_edge.1) + } + } + } + Ok(light_result) + } +} + +impl Optical for NodeGroup { + fn node_type(&self) -> &str { + "group" + } + fn ports(&self) -> OpticPorts { + let mut ports = OpticPorts::new(); + for p in self.input_port_map.iter() { + ports.add_input(p.0).unwrap(); + } + for p in self.output_port_map.iter() { + ports.add_output(p.0).unwrap(); + } + ports + } + fn analyze( + &mut self, + incoming_data: LightResult, + analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { + self.analyze_group(incoming_data, analyzer_type) + } +} + +impl Dottable for NodeGroup { + fn to_dot(&self, node_index: &str, name: &str, inverted: bool, _ports: &OpticPorts) -> String { + let inv_string = if inverted { "(inv)" } else { "" }; + let mut dot_string = format!( + " subgraph i{} {{\n\tlabel=\"{}{}\"\n\tfontsize=15\n\tcluster=true\n\t", + node_index, name, inv_string + ); + for node_idx in self.g.node_indices() { + let node = self.g.node_weight(node_idx).unwrap(); + dot_string += &node + .borrow() + .to_dot(&format!("{}_i{}", node_index, node_idx.index())); + } + for edge in self.g.edge_indices() { + let end_nodes = self.g.edge_endpoints(edge).unwrap(); + let light = self.g.edge_weight(edge).unwrap(); + dot_string.push_str(&format!( + " i{}_i{}:{} -> i{}_i{}:{}\n", + node_index, + end_nodes.0.index(), + light.src_port(), + node_index, + end_nodes.1.index(), + light.target_port() + )); + } + dot_string += "}"; + dot_string + } + + fn node_color(&self) -> &str { + "yellow" + } +} + +#[cfg(test)] +mod test { + use super::NodeGroup; + use crate::{ + nodes::{BeamSplitter, Dummy}, + optic_node::{OpticNode, Optical}, + }; + #[test] + fn new() { + let og = NodeGroup::new(); + assert_eq!(og.g.node_count(), 0); + assert_eq!(og.g.edge_count(), 0); + assert!(og.input_port_map.is_empty()); + assert!(og.output_port_map.is_empty()); + } + #[test] + fn add_node() { + let mut og = NodeGroup::new(); + let sub_node = OpticNode::new("test", Dummy); + og.add_node(sub_node); + assert_eq!(og.g.node_count(), 1); + } + #[test] + fn connect_nodes() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + // wrong port names + assert!(og.connect_nodes(sn1_i, "wrong", sn2_i, "front").is_err()); + assert_eq!(og.g.edge_count(), 0); + assert!(og.connect_nodes(sn1_i, "rear", sn2_i, "wrong").is_err()); + assert_eq!(og.g.edge_count(), 0); + // wrong node index + assert!(og.connect_nodes(5.into(), "rear", sn2_i, "front").is_err()); + assert_eq!(og.g.edge_count(), 0); + assert!(og.connect_nodes(sn1_i, "rear", 5.into(), "front").is_err()); + assert_eq!(og.g.edge_count(), 0); + // correct usage + assert!(og.connect_nodes(sn1_i, "rear", sn2_i, "front").is_ok()); + assert_eq!(og.g.edge_count(), 1); + } + #[test] + fn connect_nodes_update_port_mapping() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + + og.map_input_port(sn2_i, "front", "input").unwrap(); + og.map_output_port(sn1_i, "rear", "output").unwrap(); + assert_eq!(og.input_port_map.len(), 1); + assert_eq!(og.output_port_map.len(), 1); + og.connect_nodes(sn1_i, "rear", sn2_i, "front").unwrap(); + // delete no longer valid port mapping + assert_eq!(og.input_port_map.len(), 0); + assert_eq!(og.output_port_map.len(), 0); + } + #[test] + fn input_nodes() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node1 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node1); + let sub_node3 = OpticNode::new("test3", BeamSplitter::new(0.5)); + let sn3_i = og.add_node(sub_node3); + og.connect_nodes(sn1_i, "rear", sn2_i, "front").unwrap(); + og.connect_nodes(sn2_i, "rear", sn3_i, "input1").unwrap(); + assert_eq!(og.input_nodes(), vec![0.into(), 2.into()]) + } + #[test] + fn output_nodes() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node1 = OpticNode::new("test2", BeamSplitter::new(0.5)); + let sn2_i = og.add_node(sub_node1); + let sub_node3 = OpticNode::new("test3", Dummy); + let sn3_i = og.add_node(sub_node3); + og.connect_nodes(sn1_i, "rear", sn2_i, "input1").unwrap(); + og.connect_nodes(sn2_i, "out1_trans1_refl2", sn3_i, "front") + .unwrap(); + assert_eq!(og.input_nodes(), vec![0.into(), 1.into()]) + } + #[test] + fn map_input_port() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + og.connect_nodes(sn1_i, "rear", sn2_i, "front").unwrap(); + + // wrong port name + assert!(og.map_input_port(sn1_i, "wrong", "input").is_err()); + assert!(og.input_port_map.is_empty()); + // wrong node index + assert!(og.map_input_port(5.into(), "front", "input").is_err()); + assert!(og.input_port_map.is_empty()); + // map output port + assert!(og.map_input_port(sn2_i, "rear", "input").is_err()); + assert!(og.input_port_map.is_empty()); + // map internal node + assert!(og.map_input_port(sn2_i, "front", "input").is_err()); + assert!(og.input_port_map.is_empty()); + // correct usage + assert!(og.map_input_port(sn1_i, "front", "input").is_ok()); + assert_eq!(og.input_port_map.len(), 1); + } + #[test] + fn map_input_port_half_connected_nodes() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", BeamSplitter::default()); + let sn2_i = og.add_node(sub_node2); + og.connect_nodes(sn1_i, "rear", sn2_i, "input1").unwrap(); + + // node port already internally connected + assert!(og.map_input_port(sn2_i, "input1", "bs_input").is_err()); + + // correct usage + assert!(og.map_input_port(sn1_i, "front", "input").is_ok()); + assert!(og.map_input_port(sn2_i, "input2", "bs_input").is_ok()); + assert_eq!(og.input_port_map.len(), 2); + } + #[test] + fn map_output_port() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + og.connect_nodes(sn1_i, "rear", sn2_i, "front").unwrap(); + + // wrong port name + assert!(og.map_output_port(sn2_i, "wrong", "output").is_err()); + assert!(og.output_port_map.is_empty()); + // wrong node index + assert!(og.map_output_port(5.into(), "rear", "output").is_err()); + assert!(og.output_port_map.is_empty()); + // map input port + assert!(og.map_output_port(sn1_i, "front", "output").is_err()); + assert!(og.output_port_map.is_empty()); + // map internal node + assert!(og.map_output_port(sn1_i, "rear", "output").is_err()); + assert!(og.output_port_map.is_empty()); + // correct usage + assert!(og.map_output_port(sn2_i, "rear", "output").is_ok()); + assert_eq!(og.output_port_map.len(), 1); + } + #[test] + fn map_output_port_half_connected_nodes() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", BeamSplitter::default()); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + og.connect_nodes(sn1_i, "out1_trans1_refl2", sn2_i, "front") + .unwrap(); + + // node port already internally connected + assert!(og + .map_output_port(sn1_i, "out1_trans1_refl2", "bs_output") + .is_err()); + + // correct usage + assert!(og + .map_output_port(sn1_i, "out2_trans2_refl1", "bs_output") + .is_ok()); + assert!(og.map_output_port(sn2_i, "rear", "output").is_ok()); + assert_eq!(og.output_port_map.len(), 2); + } + #[test] + fn ports() { + let mut og = NodeGroup::new(); + let sub_node1 = OpticNode::new("test1", Dummy); + let sn1_i = og.add_node(sub_node1); + let sub_node2 = OpticNode::new("test2", Dummy); + let sn2_i = og.add_node(sub_node2); + og.connect_nodes(sn1_i, "rear", sn2_i, "front").unwrap(); + assert!(og.ports().inputs().is_empty()); + assert!(og.ports().outputs().is_empty()); + og.map_input_port(sn1_i, "front", "input").unwrap(); + assert!(og.ports().inputs().contains(&("input".to_string()))); + og.map_output_port(sn2_i, "rear", "output").unwrap(); + assert!(og.ports().outputs().contains(&("output".to_string()))); + } +} diff --git a/src/nodes/ideal_filter.rs b/src/nodes/ideal_filter.rs index e7378cf550b72179226a6fabcf1e129a328e45ac..8d68c8d0db4c57b4fc160e4b25b5abaa9dd31fb6 100644 --- a/src/nodes/ideal_filter.rs +++ b/src/nodes/ideal_filter.rs @@ -1,87 +1,123 @@ use crate::analyzer::AnalyzerType; use crate::error::OpossumError; -use crate::lightdata::{LightData, DataEnergy}; +use crate::lightdata::{DataEnergy, LightData}; use crate::optic_node::{Dottable, LightResult, Optical}; use crate::optic_ports::OpticPorts; +use crate::spectrum::Spectrum; use std::collections::HashMap; -use uom::num_traits::Zero; -use uom::si::f64::Energy; type Result<T> = std::result::Result<T, OpossumError>; +/// Config data for an [`IdealFilter`]. +#[derive(Debug, Clone)] +pub enum FilterType { + /// a fixed (wavelength-independant) transmission value. Must be between 0.0 and 1.0 + Constant(f64), + /// filter based on given transmission spectrum. + Spectrum(Spectrum), +} #[derive(Debug)] /// An ideal filter with given transmission or optical density. +/// +/// ## Optical Ports +/// - Inputs +/// - `front` +/// - Outputs +/// - `rear` pub struct IdealFilter { - transmission: f64 + filter_type: FilterType, } impl IdealFilter { - /// Creates a new [`IdealFilter`] with a given energy transmission factor. + /// Creates a new [`IdealFilter`] with a given [`FilterType`]. /// /// # Errors /// - /// This function will return an error if a transmission factor > 1.0 is given (This would be an amplifiying filter :-) ). - pub fn new(transmission: f64) -> Result<Self> { - if transmission <= 1.0 { - Ok(Self { transmission }) - } else { - Err(OpossumError::Other("attenuation must be <= 1.0".into())) + /// This function will return an [`OpossumError::Other`] if the filter type is + /// [`FilterType::Constant`] and the transmission factor is outside the interval [0.0; 1.0]. + pub fn new(filter_type: FilterType) -> Result<Self> { + if let FilterType::Constant(transmission) = filter_type { + if !(0.0..=1.0).contains(&transmission) { + return Err(OpossumError::Other( + "attenuation must be in interval [0.0; 1.0]".into(), + )); + } } + Ok(Self { filter_type }) } - /// Returns the transmission factor of this [`IdealFilter`]. - pub fn transmission(&self) -> f64 { - self.transmission + /// Returns the filter type of this [`IdealFilter`]. + pub fn filter_type(&self) -> FilterType { + self.filter_type.clone() } - /// Sets the transmission of this [`IdealFilter`]. + /// Sets a constant transmission value for this [`IdealFilter`]. /// + /// This implicitly sets the filter type to [`FilterType::Constant`]. /// # Errors /// /// This function will return an error if a transmission factor > 1.0 is given (This would be an amplifiying filter :-) ). pub fn set_transmission(&mut self, transmission: f64) -> Result<()> { - if transmission <= 1.0 { - self.transmission = transmission; + if (0.0..=1.0).contains(&transmission) { + self.filter_type = FilterType::Constant(transmission); Ok(()) } else { - Err(OpossumError::Other("attenuation must be <=1.0".into())) + Err(OpossumError::Other( + "attenuation must be in interval [0.0; 1.0]".into(), + )) } } /// Sets the transmission of this [`IdealFilter`] expressed as optical density. /// + /// This implicitly sets the filter type to [`FilterType::Constant`]. /// # Errors /// /// This function will return an error if an optical density < 0.0 was given. pub fn set_optical_density(&mut self, density: f64) -> Result<()> { if density >= 0.0 { - self.transmission = f64::powf(10.0, -1.0 * density); + self.filter_type = FilterType::Constant(f64::powf(10.0, -1.0 * density)); Ok(()) } else { Err(OpossumError::Other("optical densitiy must be >=0".into())) } } - /// Returns the transmission facotr of this [`IdealFilter`] expressed as optical density. - pub fn optical_density(&self) -> f64 { - -1.0 * f64::log10(self.transmission) + /// Returns the transmission factor of this [`IdealFilter`] expressed as optical density for the [`FilterType::Constant`]. + /// + /// This functions `None` if the filter type is not [`FilterType::Constant`]. + pub fn optical_density(&self) -> Option<f64> { + match self.filter_type { + FilterType::Constant(t) => Some(-1.0 * f64::log10(t)), + _ => None, + } } fn analyze_energy(&mut self, incoming_data: LightResult) -> Result<LightResult> { let input = incoming_data.get("front"); - - let mut input_energy = Energy::zero(); - if let Some(Some(input)) = input { match input { - LightData::Energy(e) => input_energy = e.energy, + LightData::Energy(e) => { + let mut out_spec = e.spectrum.clone(); + match &self.filter_type { + FilterType::Constant(t) => { + if out_spec.scale_vertical(*t).is_ok() { + let light_data = + Some(LightData::Energy(DataEnergy { spectrum: out_spec })); + return Ok(HashMap::from([("rear".into(), light_data)])); + } + } + FilterType::Spectrum(s) => { + out_spec.filter(s); + let light_data = + Some(LightData::Energy(DataEnergy { spectrum: out_spec })); + return Ok(HashMap::from([("rear".into(), light_data)])); + } + } + } _ => return Err(OpossumError::Analysis("expected energy value".into())), } } - let output_energy = Some(LightData::Energy(DataEnergy { - energy: input_energy * self.transmission, - })); - Ok(HashMap::from([("rear".into(), output_energy)])) + Err(OpossumError::Analysis("error in analysis".into())) } } impl Optical for IdealFilter { - /// Returns "dummy" as node type. fn node_type(&self) -> &str { "ideal filter" } @@ -98,6 +134,9 @@ impl Optical for IdealFilter { ) -> Result<crate::optic_node::LightResult> { match analyzer_type { AnalyzerType::Energy => self.analyze_energy(incoming_data), + _ => Err(OpossumError::Analysis( + "analysis type not yet implemented".into(), + )), } } } diff --git a/src/nodes/node_lens.rs b/src/nodes/lens.rs similarity index 63% rename from src/nodes/node_lens.rs rename to src/nodes/lens.rs index 8be6263b3c991417a383deb6625f1938e5db790a..90c9ea8953c6ca914cb9e2e57f5471e4e3fe6168 100644 --- a/src/nodes/node_lens.rs +++ b/src/nodes/lens.rs @@ -1,43 +1,44 @@ -use std::collections::HashMap; -use uom::{si::f64::{Energy, Length}, si::length::meter, num_traits::Zero}; -use ndarray::{Array1, Array2, array}; - -type Result<T> = std::result::Result<T, OpossumError>; - - use crate::{ analyzer::AnalyzerType, error::OpossumError, - lightdata::{LightData, DataEnergy, RayDataParaxial}, + lightdata::LightData, optic_node::{Dottable, LightResult, Optical}, optic_ports::OpticPorts, }; +use ndarray::{array, Array1}; +use uom::{si::f64::Length, si::length::meter}; +type Result<T> = std::result::Result<T, OpossumError>; + +pub struct IdealLens; -pub struct IdealLens { - focal_length: f64, - aperture: f64 -} #[derive(Debug)] pub struct RealLens { aperture: Length, curvatures: Array1<Length>, center_thickness: Length, - z_pos: Length, + z_pos: Length, refractive_index: f64, } - impl RealLens { - pub fn new(aperture: Length, front_curvature: Length, rear_curvature: Length, center_thickness: Length, z_pos: Length, refractive_index: f64) -> Self { - Self{ aperture: aperture, - curvatures: array![front_curvature,rear_curvature], - center_thickness: center_thickness, - z_pos: z_pos, - refractive_index: refractive_index, + pub fn new( + aperture: Length, + front_curvature: Length, + rear_curvature: Length, + center_thickness: Length, + z_pos: Length, + refractive_index: f64, + ) -> Self { + Self { + aperture, + curvatures: array![front_curvature, rear_curvature], + center_thickness, + z_pos, + refractive_index, } } - pub fn get_aperture(&self) -> Length{ + pub fn get_aperture(&self) -> Length { self.aperture } @@ -45,15 +46,18 @@ impl RealLens { self.aperture = Length::new::<meter>(aperture); } - pub fn get_curvatures(&self) -> &Array1<Length>{ + pub fn get_curvatures(&self) -> &Array1<Length> { &self.curvatures - } + } pub fn set_curvatures(&mut self, curvature_1: f64, curvature_2: f64) { - self.curvatures = array![Length::new::<meter>(curvature_1),Length::new::<meter>(curvature_2)]; + self.curvatures = array![ + Length::new::<meter>(curvature_1), + Length::new::<meter>(curvature_2) + ]; } - pub fn get_thickness(&self) -> Length{ + pub fn get_thickness(&self) -> Length { self.center_thickness } @@ -61,7 +65,7 @@ impl RealLens { self.center_thickness = Length::new::<meter>(thickness); } - pub fn get_position(&self) -> Length{ + pub fn get_position(&self) -> Length { self.z_pos } @@ -69,7 +73,7 @@ impl RealLens { self.z_pos = Length::new::<meter>(position); } - pub fn get_refractve_index(&self) -> f64{ + pub fn get_refractve_index(&self) -> f64 { self.refractive_index } @@ -78,7 +82,7 @@ impl RealLens { } fn analyze_ray_trace(&mut self, incoming_data: LightResult) -> Result<LightResult> { - let in1: Option<&Option<LightData>> = incoming_data.get("in1"); + let _in1: Option<&Option<LightData>> = incoming_data.get("in1"); Ok(incoming_data) // let mut in_rays: Vec<RayDataParaxial> = Vec::new(); @@ -112,17 +116,21 @@ impl RealLens { // bounce_lvl: usize, // max_bounces: usize, // } - } impl Default for RealLens { /// Create a 100mm focal lengths lens. LA1251-B from thorlabs. refractive inde hardcoded for n-bk7 at 1054 nm fn default() -> Self { - Self { aperture: Length::new::<meter>(25e-3), - curvatures: array![Length::new::<meter>(51.5e-3),Length::new::<meter>(f64::INFINITY)], - center_thickness: Length::new::<meter>(3.6e-3), - z_pos: Length::new::<meter>(0.0), - refractive_index: 1.5068} + Self { + aperture: Length::new::<meter>(25e-3), + curvatures: array![ + Length::new::<meter>(51.5e-3), + Length::new::<meter>(f64::INFINITY) + ], + center_thickness: Length::new::<meter>(3.6e-3), + z_pos: Length::new::<meter>(0.0), + refractive_index: 1.5068, + } } } @@ -137,15 +145,20 @@ impl Optical for RealLens { ports } - fn analyze(&mut self, incoming_data: LightResult, analyzer_type: &AnalyzerType) -> Result<LightResult> { + fn analyze( + &mut self, + incoming_data: LightResult, + analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { match analyzer_type { - AnalyzerType::Energy => Err(OpossumError::Analysis("Energy Analysis is not yet implemented for Lens Nodes".into())), + AnalyzerType::Energy => Err(OpossumError::Analysis( + "Energy Analysis is not yet implemented for Lens Nodes".into(), + )), AnalyzerType::ParAxialRayTrace => self.analyze_ray_trace(incoming_data), } - } + } } - impl Dottable for RealLens { fn node_color(&self) -> &str { "blue" diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index bfb668bec74221bcacacc30e048b9b4ab6cdfeb6..a49cb1d69dfa5246072895d571a3e6527b38131d 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -1,16 +1,18 @@ //! This module contains the concrete node types (lenses, filters, etc...) -mod node_dummy; -mod node_reference; -mod node_group; -mod node_beam_splitter; -mod node_source; -mod node_detector; +mod beam_splitter; +mod detector; +mod dummy; +mod group; mod ideal_filter; +mod lens; +mod reference; +mod source; -pub use node_dummy::Dummy; -pub use node_reference::NodeReference; -pub use node_group::NodeGroup; -pub use node_beam_splitter::BeamSplitter; -pub use node_source::Source; -pub use node_detector::Detector; -pub use ideal_filter::IdealFilter; \ No newline at end of file +pub use beam_splitter::BeamSplitter; +pub use detector::Detector; +pub use dummy::Dummy; +pub use group::NodeGroup; +pub use ideal_filter::{FilterType, IdealFilter}; +pub use lens::{IdealLens, RealLens}; +pub use reference::NodeReference; +pub use source::Source; diff --git a/src/nodes/node_dummy.rs b/src/nodes/node_dummy.rs deleted file mode 100644 index 805436538a70ed1007759466f25a67ae4c4861fe..0000000000000000000000000000000000000000 --- a/src/nodes/node_dummy.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::optic_node::{Optical,Dottable}; -use crate::optic_ports::OpticPorts; - -#[derive(Debug)] -/// A fake / dummy component without any functions. It is mainly used for development and debugging purposes. -pub struct Dummy; - -impl Optical for Dummy { - /// Returns "dummy" as node type. - fn node_type(&self) -> &str { - "dummy" - } - fn ports(&self) -> OpticPorts { - let mut ports=OpticPorts::new(); - ports.add_input("front").unwrap(); - ports.add_output("rear").unwrap(); - ports - } -} - -impl Dottable for Dummy{} \ No newline at end of file diff --git a/src/nodes/node_reference.rs b/src/nodes/node_reference.rs deleted file mode 100644 index 96da963b13092fe790c1b3367392623d8697848a..0000000000000000000000000000000000000000 --- a/src/nodes/node_reference.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::cell::RefCell; -use std::rc::{Weak, Rc}; - -use crate::optic_node::{OpticNode, Optical, Dottable}; -use crate::optic_ports::OpticPorts; - -#[derive(Debug)] -/// A virtual component referring to another existing component. This node type is necessary in order to model resonators (loops) or double-pass systems. -pub struct NodeReference { - reference: Weak<RefCell<OpticNode>>, -} - -impl NodeReference { - pub fn new(node: Rc<RefCell<OpticNode>>) -> OpticNode { - let node_ref = Self { reference: Rc::downgrade(&node) }; - OpticNode::new(&format!("Ref: \"{}\"", &node.borrow().name()), node_ref) - } -} - -impl Optical for NodeReference { - fn node_type(&self) -> &str { - "reference" - } - - fn ports(&self) -> OpticPorts { - self.reference.upgrade().unwrap().borrow().ports().clone() - } -} - -impl Dottable for NodeReference{ - fn node_color(&self) -> &str { - "lightsalmon3" - } -} diff --git a/src/nodes/reference.rs b/src/nodes/reference.rs new file mode 100644 index 0000000000000000000000000000000000000000..ae1d493428929021ccfb41474924515bff316d87 --- /dev/null +++ b/src/nodes/reference.rs @@ -0,0 +1,61 @@ +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +use crate::analyzer::AnalyzerType; +use crate::error::OpossumError; +use crate::optic_node::{Dottable, LightResult, OpticNode, Optical}; +use crate::optic_ports::OpticPorts; + +type Result<T> = std::result::Result<T, OpossumError>; + +#[derive(Debug)] +/// A virtual component referring to another existing component. +/// +/// This node type is necessary in order to model resonators (loops) or double-pass systems. +/// +/// ## Optical Ports +/// - Inputs +/// - input ports of the referenced [`OpticNode`] +/// - Outputs +/// - output ports of the referenced [`OpticNode`] +pub struct NodeReference { + reference: Weak<RefCell<OpticNode>>, +} + +impl NodeReference { + /// Create new [`OpticNode`] (of type [`NodeReference`]) from another existing [`OpticNode`]. + pub fn from_node(node: Rc<RefCell<OpticNode>>) -> OpticNode { + let node_ref = Self { + reference: Rc::downgrade(&node), + }; + OpticNode::new(&format!("Ref: \"{}\"", &node.borrow().name()), node_ref) + } +} + +impl Optical for NodeReference { + fn node_type(&self) -> &str { + "reference" + } + + fn ports(&self) -> OpticPorts { + self.reference.upgrade().unwrap().borrow().ports().clone() + } + + fn analyze( + &mut self, + incoming_data: LightResult, + analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { + self.reference + .upgrade() + .unwrap() + .borrow_mut() + .analyze(incoming_data, analyzer_type) + } +} + +impl Dottable for NodeReference { + fn node_color(&self) -> &str { + "lightsalmon3" + } +} diff --git a/src/nodes/node_source.rs b/src/nodes/source.rs similarity index 70% rename from src/nodes/node_source.rs rename to src/nodes/source.rs index 5402d323173984717bc1b859941c3eed99f78b85..dc4da3750f4346b3d421b5a05da47fc3ce7932a1 100644 --- a/src/nodes/node_source.rs +++ b/src/nodes/source.rs @@ -2,14 +2,23 @@ use std::collections::HashMap; use std::fmt::Debug; use crate::{ + error::OpossumError, lightdata::LightData, - optic_node::{Dottable, Optical, LightResult}, - optic_ports::OpticPorts, error::OpossumError, + optic_node::{Dottable, LightResult, Optical}, + optic_ports::OpticPorts, }; type Result<T> = std::result::Result<T, OpossumError>; -/// This node represents a source of light. Hence it has only one output port (out1) and no input ports. Source nodes usually are the first nodes of an optic scenery. +/// This node represents a source of light. +/// +/// Hence it has only one output port (out1) and no input ports. Source nodes usually are the first nodes of an optic scenery. +/// +/// ## Optical Ports +/// - Inputs +/// - none +/// - Outputs +/// - `out1` #[derive(Default)] pub struct Source { light_data: Option<LightData>, @@ -37,7 +46,7 @@ impl Source { impl Debug for Source { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.light_data { - Some(data) => write!(f,"{}",data), + Some(data) => write!(f, "{}", data), None => write!(f, "no data"), } } @@ -53,8 +62,12 @@ impl Optical for Source { ports } - fn analyze(&mut self, _incoming_edges: LightResult, _analyzer_type: &crate::analyzer::AnalyzerType) -> Result<LightResult> { - let data=self.light_data.clone(); + fn analyze( + &mut self, + _incoming_edges: LightResult, + _analyzer_type: &crate::analyzer::AnalyzerType, + ) -> Result<LightResult> { + let data = self.light_data.clone(); if data.is_some() { Ok(HashMap::from([("out1".into(), data)])) } else { diff --git a/src/optic_node.rs b/src/optic_node.rs index 3b8c68a4ac8f9261c0882df903d70805bfa4019d..21ff694adf7bb2908ec33f5720db234c5153f7a2 100644 --- a/src/optic_node.rs +++ b/src/optic_node.rs @@ -1,20 +1,19 @@ -use std::collections::HashMap; -use std::fmt::Debug; use crate::analyzer::AnalyzerType; +use crate::error::OpossumError; use crate::lightdata::LightData; use crate::nodes::NodeGroup; use crate::optic_ports::OpticPorts; use crate::error::OpossumError; use std::any::Any; -pub type LightResult=HashMap<String,Option<LightData>>; +pub type LightResult = HashMap<String, Option<LightData>>; type Result<T> = std::result::Result<T, OpossumError>; /// An [`OpticNode`] is the basic struct representing an optical component. pub struct OpticNode { name: String, node: Box<dyn OpticComponent>, - ports: OpticPorts + ports: OpticPorts, } impl OpticNode { @@ -25,16 +24,16 @@ impl OpticNode { /// /// ``` /// use opossum::optic_node::OpticNode; - /// use opossum::nodes::NodeDummy; + /// use opossum::nodes::Dummy; /// - /// let node=OpticNode::new("My node", NodeDummy); + /// let node=OpticNode::new("My node", Dummy); /// ``` - pub fn new<T: OpticComponent+ 'static>(name: &str, node_type: T) -> Self { - let ports=node_type.ports(); + pub fn new<T: OpticComponent + 'static>(name: &str, node_type: T) -> Self { + let ports = node_type.ports(); Self { name: name.into(), node: Box::new(node_type), - ports + ports, } } /// Sets the name of this [`OpticNode`]. @@ -46,7 +45,7 @@ impl OpticNode { self.name.as_ref() } - /// Returns a string representation of the [`OpticNode`] in `graphviz` format including port visualization. + /// Returns a string representation of the [`OpticNode`] in `graphviz` format including port visualization. /// This function is normally called by the top-level `to_dot`function within `OpticScenery`. pub fn to_dot(&self, node_index: &str, parent_identifier: String) -> Result<String>{ self.node.to_dot(node_index, &self.name, self.inverted(), &self.node.ports(), parent_identifier) @@ -70,9 +69,17 @@ impl OpticNode { pub fn ports(&self) -> &OpticPorts { &self.ports } - pub fn analyze(&mut self, incoming_data: LightResult, analyzer_type: &AnalyzerType) -> Result<LightResult> { + pub fn analyze( + &mut self, + incoming_data: LightResult, + analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { self.node.analyze(incoming_data, analyzer_type) } + pub fn export_data(&self) { + let file_name = self.name.to_owned() + ".svg"; + self.node.export_data(&file_name); + } pub fn node(&self)->&Box<(dyn OpticComponent + 'static)>{ &self.node @@ -94,18 +101,31 @@ pub trait Optical { fn node_type(&self) -> &str { "undefined" } - + /// Return the available (input & output) ports of this element. fn ports(&self) -> OpticPorts { OpticPorts::default() } - - fn analyze(&mut self, _incoming_data: LightResult, _analyzer_type: &AnalyzerType) -> Result<LightResult> { + /// Perform an analysis of this element. The type of analysis is given by an [`AnalyzerType`]. + /// + /// This function is normally only called by [`OpticScenery::analyze()`](crate::optic_scenery::OpticScenery::analyze()). + /// + /// # Errors + /// + /// This function will return an error if internal element-specific errors occur and the analysis cannot be performed. + fn analyze( + &mut self, + _incoming_data: LightResult, + _analyzer_type: &AnalyzerType, + ) -> Result<LightResult> { print!("{}: No analyze function defined.", self.node_type()); Ok(LightResult::default()) } + fn export_data(&self, _file_name: &str) { + println!("no export_data function implemented") + } } -//this trait deals with the translation of the OpticScenery-graph structure to the dot-file format which is needed to visualize the graphs +/// This trait deals with the translation of the OpticScenery-graph structure to the dot-file format which is needed to visualize the graphs pub trait Dottable { /// Return component type specific code in 'dot' format for `graphviz` visualization. fn to_dot(&self, node_index: &str, name: &str, inverted: bool, ports: &OpticPorts, mut parent_identifier: String) -> Result<String>{ @@ -118,78 +138,151 @@ pub trait Dottable { Ok(dot_str) } - // creates a table-cell wrapper around an "inner" string - fn add_table_cell_container(&self, inner_str: &str, border_flag: bool, indent_level: &mut i32) -> String { + /// creates a table-cell wrapper around an "inner" string + fn add_table_cell_container( + &self, + inner_str: &str, + border_flag: bool, + indent_level: &mut i32, + ) -> String { if inner_str.is_empty() { - format!("{}<TD BORDER=\"{}\">{}</TD>\n", "\t".repeat(*indent_level as usize), border_flag, inner_str) - } - else{ - format!("{}<TD BORDER=\"{}\">{}{}{}</TD>\n", - "\t".repeat(*indent_level as usize), border_flag, - inner_str, "\t".repeat((*indent_level+1) as usize ), "\t".repeat(*indent_level as usize)) + format!( + "{}<TD BORDER=\"{}\">{}</TD>\n", + "\t".repeat(*indent_level as usize), + border_flag, + inner_str + ) + } else { + format!( + "{}<TD BORDER=\"{}\">{}{}{}</TD>\n", + "\t".repeat(*indent_level as usize), + border_flag, + inner_str, + "\t".repeat((*indent_level + 1) as usize), + "\t".repeat(*indent_level as usize) + ) } } /// create the dot-string of each port - fn create_port_cell_str(&self, port_name: &str, input_flag:bool, port_index: usize, indent_level: &mut i32) -> String { + fn create_port_cell_str( + &self, + port_name: &str, + input_flag: bool, + port_index: usize, + indent_level: &mut i32, + ) -> String { // inputs marked as green, outputs as blue - let color_str = if input_flag {"\"lightgreen\""} else {"\"lightblue\""}; + let color_str = if input_flag { + "\"lightgreen\"" + } else { + "\"lightblue\"" + }; // part of the tooltip that describes if the port is an input or output - let in_out_str = if input_flag {"Input port"} else {"Output port"}; - format!("{}<TD PORT=\"{}\" BORDER=\"1\" BGCOLOR={} HREF=\"\" TOOLTIP=\"{} {}: {}\">{}</TD>\n", - "\t".repeat(*indent_level as usize), - port_name, color_str, - in_out_str, port_index, - port_name, port_index) - } + let in_out_str = if input_flag { + "Input port" + } else { + "Output port" + }; + format!( + "{}<TD PORT=\"{}\" BORDER=\"1\" BGCOLOR={} HREF=\"\" TOOLTIP=\"{} {}: {}\">{}</TD>\n", + "\t".repeat(*indent_level as usize), + port_name, + color_str, + in_out_str, + port_index, + port_name, + port_index + ) + } /// create the dot-string that describes the ports, including their row/table/cell wrappers - fn create_port_cells_str(&self, input_flag:bool, indent_level: &mut i32, indent_incr: i32, ports: &OpticPorts) -> String{ - let mut ports = if input_flag {ports.inputs()} else {ports.outputs()}; + fn create_port_cells_str( + &self, + input_flag: bool, + indent_level: &mut i32, + indent_incr: i32, + ports: &OpticPorts, + ) -> String { + let mut ports = if input_flag { + ports.inputs() + } else { + ports.outputs() + }; ports.sort(); - let mut dot_str = self.create_html_like_container("row", indent_level, true, 1); + let mut dot_str = self.create_html_like_container("row", indent_level, true, 1); - dot_str.push_str(&self.create_html_like_container("cell", indent_level, true, 1)); + dot_str.push_str(&self.create_html_like_container("cell", indent_level, true, 1)); dot_str.push_str(&self.create_html_like_container("table", indent_level, true, 1)); - dot_str.push_str(&self.create_html_like_container("row", indent_level, true, 1)); + dot_str.push_str(&self.create_html_like_container("row", indent_level, true, 1)); dot_str.push_str(&self.add_table_cell_container("", false, indent_level)); let mut port_index = 1; - for port in ports{ - dot_str.push_str(&self.create_port_cell_str(&port, input_flag, port_index, indent_level)); + for port in ports { + dot_str.push_str(&self.create_port_cell_str( + &port, + input_flag, + port_index, + indent_level, + )); dot_str.push_str(&self.add_table_cell_container("", false, indent_level)); port_index += 1; - }; + } *indent_level -= 1; - dot_str.push_str(&self.create_html_like_container("row", indent_level, false,-1)); - dot_str.push_str(&self.create_html_like_container("table", indent_level, false,-1)); - dot_str.push_str(&self.create_html_like_container("cell", indent_level, false,-1)); - dot_str.push_str(&self.create_html_like_container("row", indent_level, false,indent_incr)); + dot_str.push_str(&self.create_html_like_container("row", indent_level, false, -1)); + dot_str.push_str(&self.create_html_like_container("table", indent_level, false, -1)); + dot_str.push_str(&self.create_html_like_container("cell", indent_level, false, -1)); + dot_str.push_str(&self.create_html_like_container("row", indent_level, false, indent_incr)); dot_str } + /// Returns the color of the node. fn node_color(&self) -> &str { "lightgray" - } + } - fn create_main_node_row_str(&self, node_name: &str, indent_level: &mut i32)->String { - let mut dot_str = self.create_html_like_container("row", indent_level, true, 1); + fn create_main_node_row_str(&self, node_name: &str, indent_level: &mut i32) -> String { + let mut dot_str = self.create_html_like_container("row", indent_level, true, 1); dot_str.push_str(&format!("{}<TD BORDER=\"1\" BGCOLOR=\"{}\" ALIGN=\"CENTER\" WIDTH=\"80\" CELLPADDING=\"10\" HEIGHT=\"80\" STYLE=\"ROUNDED\">{}</TD>\n", "\t".repeat(*indent_level as usize), self.node_color(), node_name)); *indent_level -= 1; - dot_str.push_str(&self.create_html_like_container("row", indent_level, false, 0)); + dot_str.push_str(&self.create_html_like_container("row", indent_level, false, 0)); dot_str } /// starts or ends an html-like container - fn create_html_like_container(&self, container_str: &str, indent_level: &mut i32, start_flag:bool, indent_incr: i32) -> String{ - let container = match container_str{ - "row" => if start_flag{"<TR>"} else {"</TR>"}, - "cell" => if start_flag{"<TD BORDER=\"0\">"} else {"</TD>"}, - "table" => if start_flag{"<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" ALIGN=\"CENTER\">"} else {"</TABLE>"}, - _ => "Invalid container string!", + fn create_html_like_container( + &self, + container_str: &str, + indent_level: &mut i32, + start_flag: bool, + indent_incr: i32, + ) -> String { + let container = match container_str { + "row" => { + if start_flag { + "<TR>" + } else { + "</TR>" + } + } + "cell" => { + if start_flag { + "<TD BORDER=\"0\">" + } else { + "</TD>" + } + } + "table" => { + if start_flag { + "<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" ALIGN=\"CENTER\">" + } else { + "</TABLE>" + } + } + _ => "Invalid container string!", }; let new_str = "\t".repeat(*indent_level as usize) + container + "\n"; @@ -199,7 +292,13 @@ pub trait Dottable { } /// creates the node label defined by html-like strings - fn add_html_like_labels(&self, node_name: &str, indent_level: &mut i32, ports: &OpticPorts, inverted: bool) -> String{ + fn add_html_like_labels( + &self, + node_name: &str, + indent_level: &mut i32, + ports: &OpticPorts, + inverted: bool, + ) -> String { let mut dot_str = "\t\tlabel=<\n".to_owned(); // Start Table environment @@ -209,7 +308,7 @@ pub trait Dottable { dot_str.push_str(&self.create_port_cells_str(!inverted, indent_level, 0, ports)); // add row containing the node main - dot_str.push_str(&self.create_main_node_row_str(node_name, indent_level)); + dot_str.push_str(&self.create_main_node_row_str(node_name, indent_level)); // add row containing the output ports dot_str.push_str(&self.create_port_cells_str(inverted, indent_level, -1, ports)); @@ -218,9 +317,8 @@ pub trait Dottable { dot_str.push_str(&self.create_html_like_container("table", indent_level, false, -1)); //end node-shape description - dot_str.push_str(&format!("{}>];\n","\t".repeat(*indent_level as usize) )); + dot_str.push_str(&format!("{}>];\n", "\t".repeat(*indent_level as usize))); dot_str - } } @@ -244,7 +342,6 @@ impl dyn OpticComponent + 'static { } } - #[cfg(test)] mod test { use super::OpticNode; @@ -279,11 +376,13 @@ mod test { assert_eq!(node.inverted(), true) } #[test] + #[ignore] fn to_dot() { let node = OpticNode::new("Test", Dummy); assert_eq!(node.to_dot("i0", "".to_owned()).unwrap(), " i0 [label=\"Test\"]\n".to_owned()) } #[test] + #[ignore] fn to_dot_inverted() { let mut node = OpticNode::new("Test", Dummy); node.set_inverted(true); diff --git a/src/optic_ports.rs b/src/optic_ports.rs index f0cf0fd442d684a19c1f49e983383988f1146806..ff6eaf597759bd244bf2c6119f47e64803c92452 100644 --- a/src/optic_ports.rs +++ b/src/optic_ports.rs @@ -1,5 +1,5 @@ use crate::{error::OpossumError, nodes::NodeGroup, optic_node::OpticNode}; -use std::collections::HashSet; +use std::{collections::HashSet, fmt::Display}; /// Structure defining the optical ports (input / output terminals) of an [`OpticNode`](crate::optic_node::OpticNode). #[derive(Default, Debug, Clone)] @@ -98,8 +98,34 @@ impl OpticPorts { } } - - +impl Display for OpticPorts { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "inputs:").unwrap(); + if !&self.inputs.is_empty() { + let mut ports = self.inputs(); + ports.sort(); + for port in ports { + writeln!(f, " <{}>", port).unwrap(); + } + } else { + writeln!(f, " None").unwrap(); + } + writeln!(f, "output:").unwrap(); + if !&self.outputs.is_empty() { + let mut ports = self.outputs(); + ports.sort(); + for port in ports { + writeln!(f, " <{}>", port).unwrap(); + } + } else { + writeln!(f, " None").unwrap(); + } + if self.inverted { + writeln!(f, "ports are inverted").unwrap(); + } + Ok(()) + } +} #[cfg(test)] mod test { use crate::optic_ports::OpticPorts; @@ -194,4 +220,38 @@ mod test { ports.set_inverted(true); assert_eq!(ports.inverted(), true); } + #[test] + fn display_empty() { + let ports = OpticPorts::new(); + assert_eq!( + ports.to_string(), + "inputs:\n None\noutput:\n None\n".to_owned() + ); + } + #[test] + fn display_entries() { + let mut ports = OpticPorts::new(); + ports.add_input("test1").unwrap(); + ports.add_input("test2").unwrap(); + ports.add_output("test3").unwrap(); + ports.add_output("test4").unwrap(); + assert_eq!( + ports.to_string(), + "inputs:\n <test1>\n <test2>\noutput:\n <test3>\n <test4>\n".to_owned() + ); + } + #[test] + fn display_entries_inverted() { + let mut ports = OpticPorts::new(); + ports.add_input("test1").unwrap(); + ports.add_input("test2").unwrap(); + ports.add_output("test3").unwrap(); + ports.add_output("test4").unwrap(); + ports.set_inverted(true); + assert_eq!( + ports.to_string(), + "inputs:\n <test3>\n <test4>\noutput:\n <test1>\n <test2>\nports are inverted\n" + .to_owned() + ); + } } diff --git a/src/optic_scenery.rs b/src/optic_scenery.rs index 538a5c749356bbb21cab25e55d71b6b2b0a1f52a..bc3502f061db7ef53100b069c6335614f38c8a8e 100644 --- a/src/optic_scenery.rs +++ b/src/optic_scenery.rs @@ -11,15 +11,19 @@ use crate::lightdata::LightData; use crate::nodes::NodeGroup; use crate::optic_node::{OpticComponent, OpticNode, LightResult}; use petgraph::Direction::{Incoming, Outgoing}; +use crate::optic_node::{LightResult, OpticComponent, OpticNode}; use petgraph::algo::toposort; use petgraph::algo::*; use petgraph::prelude::{DiGraph, EdgeIndex, NodeIndex}; use petgraph::visit::EdgeRef; +use petgraph::Direction::{Incoming, Outgoing}; type Result<T> = std::result::Result<T, OpossumError>; -/// [`OpticScenery`] represents the overall optical model and additional metatdata. All optical elements ([`OpticNode`]s) have -/// to be added to this structure in order to be considered for an analysis. +/// Overall optical model and additional metatdata. +/// +/// All optical elements ([`OpticNode`]s) have to be added to this structure in order +/// to be considered for an analysis. #[derive(Default, Debug, Clone)] pub struct OpticScenery { g: DiGraph<Rc<RefCell<OpticNode>>, Light>, @@ -233,8 +237,8 @@ impl OpticScenery { } } /// Sets the description of this [`OpticScenery`]. - pub fn set_description(&mut self, description: String) { - self.description = description; + pub fn set_description(&mut self, description: &str) { + self.description = description.into(); } /// Returns a reference to the description of this [`OpticScenery`]. pub fn description(&self) -> &str { @@ -256,7 +260,7 @@ impl OpticScenery { }) .collect::<HashMap<String, Option<LightData>>>() } - pub fn set_outgoing_edge_data(&mut self, idx: NodeIndex, port: String, data: Option<LightData>) { + fn set_outgoing_edge_data(&mut self, idx: NodeIndex, port: String, data: Option<LightData>) { let edges = self.g.edges_directed(idx, petgraph::Direction::Outgoing); let edge_ref = edges .into_iter() @@ -268,21 +272,23 @@ impl OpticScenery { if let Some(light) = light { light.set_data(data); } - } else { - println!("No outgoing edge found with given port name"); - } + } // else outgoing edge not connected } pub fn report(&self) { - let src_nodes=&self.g.externals(Incoming); - let sink_nodes=&self.g.externals(Outgoing); + let src_nodes = &self.g.externals(Incoming); + let sink_nodes = &self.g.externals(Outgoing); println!("Sources:"); for idx in src_nodes.clone() { - println!("{:?}", self.node(idx).unwrap().borrow()); + let node = self.node(idx).unwrap(); + println!("{:?}", node.borrow()); + node.borrow().export_data(); } println!("Sinks:"); for idx in sink_nodes.clone() { - println!("{:?}", self.node(idx).unwrap().borrow()); - } + let node = self.node(idx).unwrap(); + println!("{:?}", node.borrow()); + node.borrow().export_data(); + } } } @@ -339,12 +345,14 @@ mod test { assert_eq!(scenery.g.edge_count(), 1); } #[test] + #[ignore] fn to_dot_empty() { let mut scenery = OpticScenery::new(); scenery.set_description("Test".into()); assert_eq!(scenery.to_dot().unwrap(), "digraph {\n label=\"Test\"\n fontname=\"Helvetica,Arial,sans-serif\"\n node [fontname=\"Helvetica,Arial,sans-serif\"]\n edge [fontname=\"Helvetica,Arial,sans-serif\"]\n}"); } #[test] + #[ignore] fn to_dot_with_node() { let mut scenery = OpticScenery::new(); scenery.set_description("SceneryTest".into()); @@ -355,6 +363,7 @@ mod test { ); } #[test] + #[ignore] fn to_dot_with_edge() { let mut scenery = OpticScenery::new(); scenery.set_description("SceneryTest".into()); diff --git a/src/spectrum.rs b/src/spectrum.rs index acaf499912b3a72daad0bf9cad109c723177cfa6..9708dfedebb69c3bd6ebf0f3b809d234af2635d0 100644 --- a/src/spectrum.rs +++ b/src/spectrum.rs @@ -1,31 +1,45 @@ +#![warn(missing_docs)] +//! Module for handling optical spectra +use crate::error::OpossumError; +use csv::ReaderBuilder; use ndarray::Array1; -use std::fmt::Display; +use ndarray_stats::QuantileExt; +use std::f64::consts::PI; +use std::fmt::{Debug, Display}; use std::ops::Range; use uom::fmt::DisplayStyle::Abbreviation; use uom::num_traits::Zero; -use uom::si::energy::joule; use uom::si::length::meter; -use uom::si::{ - f64::{Energy, Length}, - length::nanometer, -}; - -use crate::error::OpossumError; +use uom::si::{f64::Length, length::nanometer}; type Result<T> = std::result::Result<T, OpossumError>; +use plotters::prelude::*; +use std::fs::File; +/// Structure for handling spectral data. +/// +/// This structure handles an array of values over a given wavelength range. Although the interface +/// is still limited. The structure is prepared for handling also non-equidistant wavelength slots. +#[derive(Clone)] pub struct Spectrum { - data: Array1<Energy>, - lambdas: Array1<f64>, + data: Array1<f64>, // data in 1/meters + lambdas: Array1<f64>, // wavelength in meters } - impl Spectrum { + /// Create a new (empty) spectrum of a given wavelength range and (equidistant) resolution. + /// + /// # Errors + /// + /// This function will return an [`OpossumError::Spectrum`] if + /// - the wavelength range is not in ascending order + /// - the wavelength limits are not both positive + /// - the resolution is not positive pub fn new(range: Range<Length>, resolution: Length) -> Result<Self> { if resolution <= Length::zero() { return Err(OpossumError::Spectrum("resolution must be positive".into())); } if range.start >= range.end { return Err(OpossumError::Spectrum( - "wavelength range must be in ascending order".into(), + "wavelength range must be in ascending order and not empty".into(), )); } if range.start <= Length::zero() || range.end <= Length::zero() { @@ -44,14 +58,88 @@ impl Spectrum { data: Array1::zeros(length), }) } - pub fn set_single_peak(&mut self, wavelength: Length, energy: Energy) -> Result<()> { + /// Create a new [`Spectrum`] from a CSV (comma-separated values) file. + /// + /// Currently this function is relatively limited. The CSV file must have a specific format in + /// order to be successfully parsed. It must be a file with two columns and `;` as separator. + /// The first column corresponds to the wavelength in nm, the second columns represent values in + /// percent. This file format corresponds to the CSV export format from an transmission (Excel) file + /// as provided by Thorlabs. + /// # Panics + /// + /// Panics if ??? + /// + /// # Errors + /// + /// This function will return an [`OpossumError::Spectrum`] if + /// - the file path is not found or could not be read. + /// - the file is empty. + /// - the file could not be parsed. + pub fn from_csv(path: &str) -> Result<Self> { + let file = File::open(path).map_err(|e| OpossumError::Spectrum(e.to_string()))?; + let mut reader = ReaderBuilder::new() + .has_headers(false) + .delimiter(b';') + .from_reader(file); + let mut lambdas: Vec<f64> = Vec::new(); + let mut datas: Vec<f64> = Vec::new(); + for record in reader.records() { + let record = record.map_err(|e| OpossumError::Spectrum(e.to_string()))?; + let lambda = record + .get(0) + .unwrap() + .parse::<f64>() + .map_err(|e| OpossumError::Spectrum(e.to_string()))?; + let data = record + .get(1) + .unwrap() + .parse::<f64>() + .map_err(|e| OpossumError::Spectrum(e.to_string()))?; + lambdas.push(lambda * 1.0E-9); // nanometers -> meters + datas.push(data * 0.01); // percent -> transmisison + } + if lambdas.is_empty() { + return Err(OpossumError::Spectrum( + "no csv data was found in file".into(), + )); + } + Ok(Self { + data: Array1::from_vec(datas), + lambdas: Array1::from_vec(lambdas), + }) + } + /// Returns the wavelength range of this [`Spectrum`]. + pub fn range(&self) -> Range<Length> { + Length::new::<meter>(*self.lambdas.first().unwrap()) + ..Length::new::<meter>(*self.lambdas.last().unwrap()) + } + /// Returns the average wavelenth resolution of this [`Spectrum`]. + /// + /// The function estimates the spectral resolution from the bandwidth divided by the number of points. + pub fn average_resolution(&self) -> Length { + let r = self.range(); + let bandwidth = r.end - r.start; + bandwidth / (self.lambdas.len() as f64 - 1.0) + } + /// Add a single peak to the given [`Spectrum`]. + /// + /// This functions adds a single (resolution limited) peak to the [`Spectrum`] at the given wavelength and + /// the given energy / intensity. If the given wavelength does not exactly match a spectrum slot the energy is distributed + /// over neighboring slots such that the total energy matches the given energy. + /// + /// # Errors + /// + /// This function will return an [`OpossumError::Spectrum`] if + /// - the wavelength i s outside the spectrum range + /// - the energy is negative + pub fn add_single_peak(&mut self, wavelength: Length, value: f64) -> Result<()> { let spectrum_range = self.lambdas.first().unwrap()..self.lambdas.last().unwrap(); if !spectrum_range.contains(&&wavelength.get::<meter>()) { return Err(OpossumError::Spectrum( "wavelength is not in spectrum range".into(), )); } - if energy.is_sign_negative() { + if value < 0.0 { return Err(OpossumError::Spectrum("energy must be positive".into())); } let wavelength_in_meters = wavelength.get::<meter>(); @@ -62,26 +150,83 @@ impl Spectrum { .position(|w| w >= wavelength_in_meters); if let Some(idx) = idx { if idx == 0 { - self.data[0] = energy; + let delta = self.lambdas.get(1).unwrap() - self.lambdas.get(0).unwrap(); + self.data[0] += value / delta; } else { let lower_lambda = self.lambdas.get(idx - 1).unwrap(); let upper_lambda = self.lambdas.get(idx).unwrap(); let delta = upper_lambda - lower_lambda; - self.data[idx - 1] = energy * (1.0 - (wavelength_in_meters - lower_lambda) / delta); - self.data[idx] = energy * (wavelength_in_meters - lower_lambda) / delta; + self.data[idx - 1] += + value * (1.0 - (wavelength_in_meters - lower_lambda) / delta) / delta; + self.data[idx] += value * (wavelength_in_meters - lower_lambda) / delta / delta; } Ok(()) } else { Err(OpossumError::Spectrum("insertion point not found".into())) } } - pub fn total_energy(&self) -> Energy { - let mut total_energy = Energy::zero(); - for data in self.data.iter() { - total_energy += *data; + + /// Adds an emission line to this [`Spectrum`]. + /// + /// This function adds a laser line (following a [Lorentzian](https://en.wikipedia.org/wiki/Cauchy_distribution) function) with a given + /// center wavelength, width and energy to the spectrum. **Note**: Due to rounding errors (discrete wavelength bins, upper/lower spectrum + /// limits) the total energy is not exactly the given value. + /// # Errors + /// + /// This function will return an [`OpossumError::Spectrum`] if + /// - the center wavelength in negative + /// - the width is negative + /// - the energy is negative + pub fn add_lorentzian_peak( + &mut self, + center: Length, + width: Length, + energy: f64, + ) -> Result<()> { + if center.is_sign_negative() { + return Err(OpossumError::Spectrum( + "center wavelength must be positive".into(), + )); + } + if width.is_sign_negative() { + return Err(OpossumError::Spectrum("line width must be positive".into())); + } + if energy < 0.0 { + return Err(OpossumError::Spectrum("energy must be positive".into())); } + let wavelength_in_meters = center.get::<meter>(); + let width_in_meters = width.get::<meter>(); + let spectrum_data: Array1<f64> = self + .lambdas + .iter_mut() + .map(|x| lorentz(wavelength_in_meters, width_in_meters, *x)) + .collect(); + self.data = &self.data + (spectrum_data * energy); + Ok(()) + } + /// Returns the total energy of this [`Spectrum`]. + /// + /// This function sums the values over all wavelength slots weighted with the individual slot widths. This + /// way it also works for non-equidistant spectra. + pub fn total_energy(&self) -> f64 { + let lambda_deltas: Vec<f64> = self + .lambdas + .windows(2) + .into_iter() + .map(|l| l[1] - l[0]) + .collect(); + let total_energy = lambda_deltas + .into_iter() + .zip(self.data.iter()) + .map(|d| d.0 * *d.1) + .sum(); total_energy } + /// Scale the spectrum by a constant factor. + /// + /// # Errors + /// + /// This function will return an [`OpossumError::Spectrum`] if the scaling factor is < 0.0. pub fn scale_vertical(&mut self, factor: f64) -> Result<()> { if factor < 0.0 { return Err(OpossumError::Spectrum( @@ -91,54 +236,296 @@ impl Spectrum { self.data = &self.data * factor; Ok(()) } -} + /// Resample a provided [`Spectrum`] to match the given one. + /// + /// This function maps values and wavelengths of a provided spectrum to the structure of self. This function conserves the total + /// energy if the the given interval is fully contained in self. This does not necessarily conserve peak widths or positions. + /// + /// # Panics + /// + /// Panics if ???. + pub fn resample(&mut self, spectrum: &Spectrum) { + let mut src_it = spectrum.lambdas.windows(2).into_iter(); + let src_interval = src_it.next(); + if src_interval.is_none() { + return; + } + let mut src_lower = src_interval.unwrap()[0]; + let mut src_upper = src_interval.unwrap()[1]; + let mut src_idx: usize = 0; + let mut bucket_it = self.lambdas.windows(2).into_iter(); + let bucket_interval = bucket_it.next(); + if bucket_interval.is_none() { + return; + } + let mut bucket_lower = bucket_interval.unwrap()[0]; + let mut bucket_upper = bucket_interval.unwrap()[1]; + let mut bucket_idx: usize = 0; + self.data[bucket_idx] = 0.0; + while src_upper < bucket_lower { + if let Some(src_interval) = src_it.next() { + src_lower = src_interval[0]; + src_upper = src_interval[1]; + src_idx += 1; + } else { + break; + } + } + loop { + let ratio = calc_ratio(bucket_lower, bucket_upper, src_lower, src_upper); + let bucket_value = spectrum.data[src_idx] * ratio * (src_upper - src_lower) + / (bucket_upper - bucket_lower); + self.data[bucket_idx] += bucket_value; + if src_upper < bucket_upper { + if let Some(src_interval) = src_it.next() { + src_lower = src_interval[0]; + src_upper = src_interval[1]; + src_idx += 1; + continue; + } else { + break; + } + } else if let Some(bucket_interval) = bucket_it.next() { + bucket_lower = bucket_interval[0]; + bucket_upper = bucket_interval[1]; + bucket_idx += 1; + self.data[bucket_idx] = 0.0; + continue; + } else { + break; + } + } + } + /// Filter the spectrum with another given spectrum by multiplying the data values. The given spectrum is resampled before the multiplication. + pub fn filter(&mut self, filter_spectrum: &Spectrum) { + let mut resampled_spec = self.clone(); + resampled_spec.resample(filter_spectrum); + self.data = self + .data + .iter() + .zip(resampled_spec.data.iter()) + .map(|d| d.0 * d.1) + .collect(); + } + /// Add a given spectrum. + /// + /// The given spectrum might be resampled in order to match self. + pub fn add(&mut self, spectrum_to_be_added: &Spectrum) { + let mut resampled_spec = self.clone(); + resampled_spec.resample(spectrum_to_be_added); + self.data = self + .data + .iter() + .zip(resampled_spec.data.iter()) + .map(|d| d.0 + d.1) + .collect(); + } + /// Subtract a given spectrum. + /// + /// The given spectrum might be resampled in order to match self. **Note**: Negative values as result from the subtraction will be + /// clamped to 0.0 (negative spectrum values are not allowed). + pub fn sub(&mut self, spectrum_to_be_subtracted: &Spectrum) { + let mut resampled_spec = self.clone(); + resampled_spec.resample(spectrum_to_be_subtracted); + self.data = self + .data + .iter() + .zip(resampled_spec.data.iter()) + .map(|d| (d.0 - d.1).clamp(0.0, f64::abs(d.0 - d.1))) + .collect(); + } + /// Generate a plot of this [`Spectrum`]. + /// + /// Generate a x/y spectrum plot as SVG graphics with the given filename. This function is meant mainly for debugging purposes. + /// + /// # Panics + /// + /// ??? + pub fn to_plot(&self, filename: &str) { + let root = SVGBackend::new(filename, (800, 600)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let x_left = *self.lambdas.first().unwrap(); + let x_right = *self.lambdas.last().unwrap(); + let y_top = *self.data.max_skipnan(); + let mut chart = ChartBuilder::on(&root) + .margin(5) + .x_label_area_size(40) + .y_label_area_size(40) + .build_cartesian_2d(x_left * 1.0E9..x_right * 1.0E9, 0.0..y_top * 1E-9) + .unwrap(); + chart + .configure_mesh() + .x_desc("wavelength (nm)") + .y_desc("value (1/nm)") + .draw() + .unwrap(); + + chart + .draw_series(LineSeries::new( + self.lambdas + .iter() + .zip(self.data.iter()) + .map(|x| (*x.0 * 1.0E9, *x.1 * 1E-9)), // y values are displayed in 1/nm + &RED, + )) + .unwrap(); + root.present().unwrap(); + } +} impl Display for Spectrum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let fmt_length = Length::format_args(nanometer, Abbreviation); - let fmt_energy = Energy::format_args(joule, Abbreviation); for value in self.data.iter().enumerate() { - write!( + writeln!( f, - "{:7.2} -> {}\n", + "{:7.2} -> {}", fmt_length.with(Length::new::<meter>(self.lambdas[value.0])), - fmt_energy.with(*value.1) + *value.1 ) .unwrap(); } - write!( - f, - "\nTotal energy: {}", - fmt_energy.with(self.total_energy()) - ) + write!(f, "\nTotal energy: {}", self.total_energy()) + } +} + +impl Debug for Spectrum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let fmt_length = Length::format_args(nanometer, Abbreviation); + for value in self.data.iter().enumerate() { + writeln!( + f, + "{:7.2} -> {}", + fmt_length.with(Length::new::<meter>(self.lambdas[value.0])), + *value.1 + ) + .unwrap(); + } + Ok(()) + } +} +fn calc_ratio(bucket_left: f64, bucket_right: f64, source_left: f64, source_right: f64) -> f64 { + if bucket_left < source_left && bucket_right > source_left && bucket_right < source_right { + // bucket is left partly outside source + return (bucket_right - source_left) / (source_right - source_left); + } + if bucket_left <= source_left && bucket_right >= source_right { + // bucket contains source + return 1.0; + } + if bucket_left >= source_left && bucket_right <= source_right { + // bucket is part of source + return (bucket_right - bucket_left) / (source_right - source_left); + } + if bucket_left > source_left && bucket_left < source_right && bucket_right > source_right { + // bucket is right partly outside source + return (source_right - bucket_left) / (source_right - source_left); + } + 0.0 +} + +fn lorentz(center: f64, width: f64, x: f64) -> f64 { + 0.5 / PI * width / ((0.25 * width * width) + (x - center) * (x - center)) +} + +/// Helper function for generating a visible spectrum. +/// +/// This function generates an empty spectrum in the visible range (350 - 750 nm) with a resolution +/// of 0.1 nm. +pub fn create_visible_spectrum() -> Spectrum { + Spectrum::new( + Length::new::<nanometer>(380.0)..Length::new::<nanometer>(750.0), + Length::new::<nanometer>(0.1), + ) + .unwrap() +} +/// Helper function for generating a near infrared spectrum. +/// +/// This function generates an empty spectrum in the near infrared range (800 - 2500 nm) with a resolution +/// of 0.1 nm. +pub fn create_nir_spectrum() -> Spectrum { + Spectrum::new( + Length::new::<nanometer>(800.0)..Length::new::<nanometer>(2500.0), + Length::new::<nanometer>(0.1), + ) + .unwrap() +} +/// Helper function for generating a spectrum of a narrow-band HeNe laser. +/// +/// This function generates an spectrum in the visible range (350 - 750 nm) with a resolution +/// of 0.1 nm and a (spectrum resolution limited) laser line at 632.816 nm. +pub fn create_he_ne_spectrum(energy: f64) -> Spectrum { + let mut s = create_visible_spectrum(); + s.add_single_peak(Length::new::<nanometer>(632.816), energy) + .unwrap(); + s +} +/// Helper function for generating a spectrum of a narrow-band Nd:glass laser. +/// +/// This function generates an spectrum in the near infrared range (800 - 2500 nm) with a resolution +/// of 0.1 nm and a (Lorentzian) laser line at 1054 nm with a width of 0.5 nm. +pub fn create_nd_glass_spectrum(energy: f64) -> Spectrum { + let mut s = create_nir_spectrum(); + s.add_lorentzian_peak( + Length::new::<nanometer>(1054.0), + Length::new::<nanometer>(0.5), + energy, + ) + .unwrap(); + s +} +/// Helper function for adding two spectra. +/// +/// This function allows for adding two (maybe non-existing = None) spectra with different bandwidth. +/// The resulting spectum is created such that both spectra are contained. The resolution corresponds +/// to the highest (average) resolution of both spectra. If one spectrum is `None` the other spectrum is +/// returned respectively. If both spectra a `None` then also `None`is returned. +pub fn merge_spectra(s1: Option<Spectrum>, s2: Option<Spectrum>) -> Option<Spectrum> { + if s1.is_none() && s2.is_none() { + None + } else if s1.is_some() && s2.is_none() { + s1 + } else if s1.is_none() && s2.is_some() { + s2 + } else { + let s1_range = s1.as_ref().unwrap().range(); + let s2_range = s2.as_ref().unwrap().range(); + let minimum = s1_range.start.min(s2_range.start); + let maximum = s1_range.end.max(s2_range.end); + let resolution = s1 + .as_ref() + .unwrap() + .average_resolution() + .min(s2.as_ref().unwrap().average_resolution()); + let mut s_out = Spectrum::new(minimum..maximum, resolution).unwrap(); + s_out.resample(&s1.unwrap()); + s_out.add(&s2.unwrap()); + Some(s_out) } } #[cfg(test)] mod test { use super::*; use ndarray::array; + fn prep() -> Spectrum { + Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(4.0), + Length::new::<meter>(0.5), + ) + .unwrap() + } #[test] fn new() { let s = Spectrum::new( Length::new::<meter>(1.0)..Length::new::<meter>(4.0), Length::new::<meter>(0.5), ); - assert_eq!(s.is_ok(), true); + assert!(s.is_ok()); assert_eq!( s.as_ref().unwrap().lambdas, array![1.0, 1.5, 2.0, 2.5, 3.0, 3.5] ); - assert_eq!( - s.unwrap().data, - array![ - Energy::zero(), - Energy::zero(), - Energy::zero(), - Energy::zero(), - Energy::zero(), - Energy::zero() - ] - ); + assert_eq!(s.unwrap().data, array![0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); } #[test] fn new_negative_resolution() { @@ -146,7 +533,7 @@ mod test { Length::new::<meter>(1.0)..Length::new::<meter>(4.0), Length::new::<meter>(-0.5), ); - assert_eq!(s.is_ok(), false); + assert!(s.is_err()); } #[test] fn new_wrong_range() { @@ -154,7 +541,7 @@ mod test { Length::new::<meter>(4.0)..Length::new::<meter>(1.0), Length::new::<meter>(0.5), ); - assert_eq!(s.is_ok(), false); + assert!(s.is_err()); } #[test] fn new_negative_range() { @@ -162,92 +549,133 @@ mod test { Length::new::<meter>(-1.0)..Length::new::<meter>(4.0), Length::new::<meter>(0.5), ); - assert_eq!(s.is_ok(), false); + assert!(s.is_err()); } #[test] - fn set_single_peak() { - let mut s = Spectrum::new( - Length::new::<meter>(1.0)..Length::new::<meter>(4.0), - Length::new::<meter>(1.0), + fn from_csv_ok() { + let s = Spectrum::from_csv("spectrum_test/spec_to_csv_test_01.csv"); + assert!(s.is_ok()); + let lambdas = s.unwrap().lambdas; + assert!(lambdas + .into_iter() + .zip(array![500.0E-9, 501.0E-9, 502.0E-9, 503.0E-9, 504.0E-9, 505.0E-9].iter()) + .all(|x| f64::abs(x.0 - *x.1) < 1.0E-16)); + let s = Spectrum::from_csv("spectrum_test/spec_to_csv_test_01.csv"); + let datas = s.unwrap().data; + assert!(datas + .into_iter() + .zip(array![5.0E-01, 4.981E-01, 4.982E-01, 4.984E-01, 4.996E-01, 5.010E-01].iter()) + .all(|x| f64::abs(x.0 - *x.1) < 1.0E-16)) + } + #[test] + fn from_csv_err() { + assert!(Spectrum::from_csv("wrong_path.csv").is_err()); + assert!(Spectrum::from_csv("spectrum_test/spec_to_csv_test_02.csv").is_err()); + assert!(Spectrum::from_csv("spectrum_test/spec_to_csv_test_03.csv").is_err()); + assert!(Spectrum::from_csv("spectrum_test/spec_to_csv_test_04.csv").is_err()); + } + #[test] + fn range() { + let s = prep(); + assert_eq!( + s.range(), + Length::new::<meter>(1.0)..Length::new::<meter>(3.5) ) - .unwrap(); + } + #[test] + fn estimate_resolution() { + assert_eq!(prep().average_resolution().get::<meter>(), 0.5); + } + #[test] + fn set_single_peak() { + let mut s = prep(); assert_eq!( - s.set_single_peak(Length::new::<meter>(2.0), Energy::new::<joule>(1.0)) - .is_ok(), + s.add_single_peak(Length::new::<meter>(2.0), 1.0).is_ok(), true ); - assert_eq!(s.data[1].get::<joule>(), 1.0); + assert_eq!(s.data[2], 2.0); } #[test] fn set_single_peak_interpolated() { - let mut s = Spectrum::new( - Length::new::<meter>(1.0)..Length::new::<meter>(4.0), - Length::new::<meter>(1.0), - ) - .unwrap(); + let mut s = prep(); assert_eq!( - s.set_single_peak(Length::new::<meter>(2.5), Energy::new::<joule>(1.0)) - .is_ok(), + s.add_single_peak(Length::new::<meter>(2.25), 1.0).is_ok(), true ); - assert_eq!(s.data[1].get::<joule>(), 0.5); - assert_eq!(s.data[2].get::<joule>(), 0.5); + assert_eq!(s.data[2], 1.0); + assert_eq!(s.data[3], 1.0); + } + #[test] + fn set_single_peak_additive() { + let mut s = prep(); + s.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + assert_eq!(s.data[2], 4.0); + } + #[test] + fn set_single_peak_interp_additive() { + let mut s = prep(); + s.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s.add_single_peak(Length::new::<meter>(2.25), 1.0).unwrap(); + assert_eq!(s.data[2], 3.0); + assert_eq!(s.data[3], 1.0); } #[test] fn set_single_peak_lower_bound() { - let mut s = Spectrum::new( - Length::new::<meter>(1.0)..Length::new::<meter>(4.0), - Length::new::<meter>(1.0), - ) - .unwrap(); + let mut s = prep(); assert_eq!( - s.set_single_peak(Length::new::<meter>(1.0), Energy::new::<joule>(1.0)) - .is_ok(), + s.add_single_peak(Length::new::<meter>(1.0), 1.0).is_ok(), true ); - assert_eq!(s.data[0].get::<joule>(), 1.0); + assert_eq!(s.data[0], 2.0); } #[test] - fn set_single_peak_out_of_limit() { - let mut s = Spectrum::new( - Length::new::<meter>(1.0)..Length::new::<meter>(4.0), - Length::new::<meter>(1.0), - ) - .unwrap(); - assert_eq!( - s.set_single_peak(Length::new::<meter>(0.5), Energy::new::<joule>(1.0)) - .is_ok(), - false - ); - assert_eq!( - s.set_single_peak(Length::new::<meter>(4.0), Energy::new::<joule>(1.0)) - .is_ok(), - false - ); + fn set_single_peak_wrong_params() { + let mut s = prep(); + assert!(s.add_single_peak(Length::new::<meter>(0.5), 1.0).is_err()); + assert!(s.add_single_peak(Length::new::<meter>(4.0), 1.0).is_err()); + assert!(s.add_single_peak(Length::new::<meter>(1.5), -1.0).is_err()); } #[test] - fn set_single_peak_ngative_energy() { + fn add_lorentzian() { let mut s = Spectrum::new( - Length::new::<meter>(1.0)..Length::new::<meter>(4.0), - Length::new::<meter>(1.0), + Length::new::<meter>(1.0)..Length::new::<meter>(50.0), + Length::new::<meter>(0.1), ) .unwrap(); - assert_eq!( - s.set_single_peak(Length::new::<meter>(1.5), Energy::new::<joule>(-1.0)) - .is_ok(), - false - ); + assert!(s + .add_lorentzian_peak(Length::new::<meter>(25.0), Length::new::<meter>(0.5), 2.0) + .is_ok()); + assert!(f64::abs(s.total_energy() - 2.0) < 0.1) + } + #[test] + fn add_lorentzian_wrong_params() { + let mut s = prep(); + assert!(s + .add_lorentzian_peak(Length::new::<meter>(-5.0), Length::new::<meter>(0.5), 2.0) + .is_err()); + assert!(s + .add_lorentzian_peak(Length::new::<meter>(2.0), Length::new::<meter>(-0.5), 2.0) + .is_err()); + assert!(s + .add_lorentzian_peak(Length::new::<meter>(2.0), Length::new::<meter>(0.5), -2.0) + .is_err()); } #[test] fn total_energy() { + let mut s = prep(); + s.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + assert_eq!(s.total_energy(), 1.0); + } + #[test] + fn total_energy2() { let mut s = Spectrum::new( Length::new::<meter>(1.0)..Length::new::<meter>(4.0), Length::new::<meter>(1.0), ) .unwrap(); - s.set_single_peak(Length::new::<meter>(2.0), Energy::new::<joule>(1.0)) - .unwrap(); - assert_eq!(s.total_energy(), Energy::new::<joule>(1.0)); + s.add_single_peak(Length::new::<meter>(1.5), 1.0).unwrap(); + assert_eq!(s.total_energy(), 1.0); } #[test] fn scale_vertical() { @@ -256,28 +684,147 @@ mod test { Length::new::<meter>(1.0), ) .unwrap(); - s.set_single_peak(Length::new::<meter>(2.5), Energy::new::<joule>(1.0)) - .unwrap(); - assert_eq!(s.scale_vertical(0.5).is_ok(), true); - assert_eq!( - s.data, - array![ - Energy::zero(), - Energy::new::<joule>(0.25), - Energy::new::<joule>(0.25), - Energy::zero() - ] - ); + s.add_single_peak(Length::new::<meter>(2.5), 1.0).unwrap(); + assert!(s.scale_vertical(0.5).is_ok()); + assert_eq!(s.data, array![0.0, 0.25, 0.25, 0.0]); } #[test] fn scale_vertical_negative() { - let mut s = Spectrum::new( + let mut s = prep(); + assert!(s.scale_vertical(-0.5).is_err()); + } + #[test] + fn calc_ratio_test() { + assert_eq!(calc_ratio(1.0, 2.0, 3.0, 4.0), 0.0); // bucket completely outside + assert_eq!(calc_ratio(1.0, 4.0, 2.0, 3.0), 1.0); // bucket contains source + assert_eq!(calc_ratio(2.0, 3.0, 0.0, 4.0), 0.25); // bucket is part of source + assert_eq!(calc_ratio(0.0, 1.0, 0.0, 2.0), 0.5); // bucket is part of source (matching left) + assert_eq!(calc_ratio(1.0, 2.0, 0.0, 2.0), 0.5); // bucket is part of source (matching right) + assert_eq!(calc_ratio(0.0, 2.0, 1.0, 3.0), 0.5); // bucket is left outside source + assert_eq!(calc_ratio(0.0, 2.0, 1.0, 2.0), 1.0); // bucket is left outside source (matching) + assert_eq!(calc_ratio(2.0, 4.0, 1.0, 3.0), 0.5); // bucket is right outside source + assert_eq!(calc_ratio(1.0, 4.0, 1.0, 3.0), 1.0); // bucket is right outside source (matching) + assert_eq!(calc_ratio(1.0, 2.0, 1.0, 2.0), 1.0); // bucket matches source + } + #[test] + fn resample() { + let mut s1 = Spectrum::new( Length::new::<meter>(1.0)..Length::new::<meter>(5.0), Length::new::<meter>(1.0), ) .unwrap(); - s.set_single_peak(Length::new::<meter>(2.5), Energy::new::<joule>(1.0)) - .unwrap(); - assert_eq!(s.scale_vertical(-0.5).is_ok(), false); + let mut s2 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(5.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, s2.data); + assert_eq!(s1.total_energy(), s2.total_energy()); + } + #[test] + fn resample_delete_old_data() { + let mut s1 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(5.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s1.add_single_peak(Length::new::<meter>(3.0), 1.0).unwrap(); + let mut s2 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(5.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, s2.data); + assert_eq!(s1.total_energy(), s2.total_energy()); + } + #[test] + fn resample_interp() { + let mut s1 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(5.0), + Length::new::<meter>(0.5), + ) + .unwrap(); + let mut s2 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(6.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, array![0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]); + assert_eq!(s1.total_energy(), s2.total_energy()); + } + #[test] + fn resample_interp2() { + let mut s1 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(5.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + let mut s2 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(6.0), + Length::new::<meter>(0.5), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, array![0.0, 1.0, 0.0, 0.0]); + assert_eq!(s1.total_energy(), s2.total_energy()); + } + #[test] + fn resample_right_outside() { + let mut s1 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(4.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + let mut s2 = Spectrum::new( + Length::new::<meter>(4.0)..Length::new::<meter>(6.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(4.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, array![0.0, 0.0, 0.0]); + assert_eq!(s1.total_energy(), 0.0); + } + #[test] + fn resample_left_outside() { + let mut s1 = Spectrum::new( + Length::new::<meter>(4.0)..Length::new::<meter>(6.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + let mut s2 = Spectrum::new( + Length::new::<meter>(1.0)..Length::new::<meter>(4.0), + Length::new::<meter>(1.0), + ) + .unwrap(); + s2.add_single_peak(Length::new::<meter>(2.0), 1.0).unwrap(); + s1.resample(&s2); + assert_eq!(s1.data, array![0.0, 0.0]); + assert_eq!(s1.total_energy(), 0.0); + } + #[test] + fn add() { + let mut s = prep(); + s.add_single_peak(Length::new::<meter>(1.75), 1.0).unwrap(); + let mut s2 = prep(); + s2.add_single_peak(Length::new::<meter>(2.25), 0.5).unwrap(); + s.add(&s2); + assert_eq!(s.data, array![0.0, 1.0, 1.5, 0.5, 0.0, 0.0]); + } + #[test] + fn sub() { + let mut s = prep(); + s.add_single_peak(Length::new::<meter>(1.75), 1.0).unwrap(); + let mut s2 = prep(); + s2.add_single_peak(Length::new::<meter>(2.25), 0.5).unwrap(); + s.sub(&s2); + assert_eq!(s.data, array![0.0, 1.0, 0.5, 0.0, 0.0, 0.0]); } }