├── .gitignore ├── AUTHORS ├── data ├── sequences │ ├── closure-seq1.png │ ├── closure-seq2.png │ ├── rainflow-seq1.txt │ ├── rainflow-seq6.txt │ ├── rainflow-seq3.txt │ └── rainflow-seq2.txt └── dadn │ └── barter14.dadn ├── src ├── lib.rs ├── numbers.rs ├── factors.rs ├── plastic.rs ├── tag.rs ├── compact.rs ├── description.md ├── vector.rs ├── sweep.rs ├── table_gsl.rs ├── table_bspline.rs ├── optimise_gsl.rs ├── io.rs ├── table.rs ├── options.rs ├── fracto.rs ├── list.rs ├── nelder.rs └── grow.rs ├── tests ├── plot.py └── test-comparison ├── doc ├── dadn.md ├── summary.md ├── sequences.md ├── grow.md ├── optimisation.md ├── fracto.md ├── help.md └── list.md ├── Cargo.toml ├── LICENSE-MIT └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *~ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Easigrow was created by Paul White. 2 | Contributors to the code are listed here. 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/sequences/closure-seq1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/needsglasses/easigrow/HEAD/data/sequences/closure-seq1.png -------------------------------------------------------------------------------- /data/sequences/closure-seq2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/needsglasses/easigrow/HEAD/data/sequences/closure-seq2.png -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! fatigue 2 | //! A library of useful fatigue functions 3 | //! Paul White (2015) 4 | 5 | pub static COMMENT: &'static str = "# "; 6 | 7 | extern crate svg; 8 | extern crate log; 9 | 10 | #[cfg(not(feature = "GSL"))] 11 | extern crate bspline; 12 | 13 | #[cfg(not(feature = "GSL"))] 14 | pub mod table_bspline; 15 | 16 | #[cfg(feature = "GSL")] 17 | pub mod table_gsl; 18 | 19 | pub mod beta; 20 | pub mod cycle; 21 | pub mod dadn; 22 | pub mod grow; 23 | pub mod tag; 24 | pub mod io; 25 | pub mod plastic; 26 | pub mod material; 27 | pub mod fracto; 28 | pub mod numbers; 29 | pub mod table; 30 | -------------------------------------------------------------------------------- /tests/plot.py: -------------------------------------------------------------------------------- 1 | from pylab import * 2 | import numpy as np 3 | 4 | ion() 5 | 6 | x = np.genfromtxt("test-comparison",skip_header=1,names=True) 7 | 8 | figure() 9 | title('Easigrow comparison using --dadn white:barter14-aa7050t7451') 10 | plot( x['test'][x['coupon']==0], x['easigrow'][x['coupon']==0],'sr',label='Short crack') 11 | plot( x['test'][x['coupon']==1], x['easigrow'][x['coupon']==1],'vb',label='Middle tension') 12 | plot( x['test'][x['coupon']==2], x['easigrow'][x['coupon']==2],'og',label='Compact') 13 | plot([0,600], [0,600],'--') 14 | xlim(0,600) 15 | ylim(0,600) 16 | xlabel('Test (blocks)') 17 | ylabel('Easigrow (blocks)') 18 | legend() 19 | 20 | savefig('test-comparison.svg') 21 | -------------------------------------------------------------------------------- /doc/dadn.md: -------------------------------------------------------------------------------- 1 | # Predicting the rate of fatigue crack growth 2 | 3 | The following equations are build into the program. To add a new 4 | equation requires adding a new subroutine such as those in the 5 | `dadn.rs` file. 6 | 7 | The Paris equation is 8 | 9 | $\frac{da}{dN} = C_p \Delta K^{m_p}$ 10 | 11 | where $C_p$ and $m_p$ are constants related to the material, loading and 12 | environment. We use the subscript $p$ to indicate that they are for the 13 | Paris-Erdogen equation. It should be noted that although there are 14 | many similar equations that have $C_p$ and $m_p$ like coefficients, they cannot 15 | be individually used in other equations without obtaining all the best fit 16 | coefficients again. 17 | 18 | -------------------------------------------------------------------------------- /src/numbers.rs: -------------------------------------------------------------------------------- 1 | // NonNan numbers Floating point numbers cannot be sorted because they 2 | // can be Nan. We introduce a new type of number guaranteed not to be 3 | // an NaN to allow floats to be sorted or the maximum found. 4 | 5 | use std::cmp::Ordering; 6 | 7 | #[derive(PartialEq, PartialOrd, Clone, Debug)] 8 | pub struct NonNan(f64); 9 | 10 | impl NonNan { 11 | pub fn new(val: f64) -> Option { 12 | if val.is_nan() { 13 | None 14 | } else { 15 | Some(NonNan(val)) 16 | } 17 | } 18 | } 19 | 20 | impl Eq for NonNan {} 21 | impl Ord for NonNan { 22 | fn cmp(&self, other: &NonNan) -> Ordering { 23 | self.partial_cmp(other).unwrap() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "easigrow" 4 | version = "0.0.0" 5 | description = "A fatigue crack growth matching program" 6 | license = "MIT" 7 | authors = ["Paul White "] 8 | readme = "README.md" 9 | documentation = "README" 10 | publish = false 11 | 12 | [[bin]] 13 | name = "easigrow" 14 | path = "src/main.rs" 15 | 16 | [lib] 17 | name = "fatigue" 18 | path = "src/lib.rs" 19 | 20 | [dependencies] 21 | env_logger = "0.6.1" 22 | log = "0.4" 23 | clap = "2.32.0" 24 | rayon = "1.0.3" 25 | svg = "0.5.12" 26 | clippy = {version = "*", optional = true} 27 | GSL = {version = "*", optional = true} 28 | bspline = "0.2.2" 29 | 30 | [dev-dependencies] 31 | tempdir = "0.3" 32 | 33 | [features] 34 | default = [] 35 | v2 = ["GSL/v2"] 36 | -------------------------------------------------------------------------------- /src/factors.rs: -------------------------------------------------------------------------------- 1 | /// calculates the combination type product of a range of variables 2 | /// [a b c], 3 gives aaa, baa caa aba bba cba aca bca cca aac .... 3 | /// where np is the size of the combination tupple 4 | pub fn permutations(range: &[T], np: usize) -> Vec> { 5 | let n = range.len(); 6 | let mut factors = Vec::new(); 7 | 8 | for i in 0..n.pow(np as u32) { 9 | let mut base = Vec::new(); 10 | for j in 0..np as u32 { 11 | base.push(range[((i / n.pow(j)) % n) as usize]); 12 | } 13 | factors.push(base); 14 | } 15 | 16 | factors 17 | } 18 | 19 | #[test] 20 | fn test_permutation() { 21 | let x = vec![1.0f64, 2.0, 3.0]; 22 | let ans = vec![ 23 | vec![1.0f64, 1.0], 24 | vec![2.0, 1.0], 25 | vec![3.0, 1.0], 26 | vec![1.0, 2.0], 27 | vec![2.0, 2.0], 28 | vec![3.0, 2.0], 29 | vec![1.0, 3.0], 30 | vec![2.0, 3.0], 31 | vec![3.0, 3.0], 32 | ]; 33 | 34 | assert!(permutations(&x, 2).eq(&ans)); 35 | } 36 | -------------------------------------------------------------------------------- /data/dadn/barter14.dadn: -------------------------------------------------------------------------------- 1 | # dadn-delta_K data that fits to the Barter14 coupon data 2 | # Material: AA7050-T7451 3 | # Prepared by: D. Mongru (May 2014) 4 | # Units: dadn (m), delta_K (MPa m^0.5) 5 | # Version 1 6 | 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 7 | 1.0E-12 0.45 0.44 0.43 0.42 0.41 0.39 0.38 0.36 0.33 8 | 1.0E-11 0.73 0.72 0.70 0.69 0.67 0.64 0.62 0.58 0.54 9 | 5.0E-11 1.06 1.04 1.02 0.99 0.96 0.93 0.89 0.84 0.78 10 | 1.0E-10 1.24 1.21 1.19 1.16 1.12 1.08 1.04 0.98 0.91 11 | 5.0E-10 1.77 1.73 1.69 1.65 1.60 1.55 1.48 1.40 1.30 12 | 1.0E-09 2.11 2.07 2.02 1.97 1.91 1.85 1.77 1.67 1.55 13 | 5.0E-09 3.27 3.20 3.13 3.05 2.96 2.86 2.74 2.59 2.40 14 | 1.0E-08 4.08 4.00 3.91 3.81 3.70 3.57 3.43 3.24 3.00 15 | 5.0E-08 7.06 6.80 6.52 6.21 5.87 5.50 5.07 4.57 3.95 16 | 1.0E-07 8.59 8.20 7.78 7.33 6.85 6.31 5.72 5.03 4.20 17 | 5.0E-07 13.42 12.50 11.55 10.56 9.52 8.42 7.25 5.97 4.55 18 | 1.0E-06 15.34 14.20 13.02 11.80 10.54 9.22 7.82 6.33 4.70 19 | 5.0E-06 20.07 18.30 16.51 14.68 12.83 10.93 8.99 6.99 4.90 20 | 1.0E-05 21.45 19.50 17.53 15.53 13.51 11.46 9.36 7.22 5.00 21 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Commonwealth of Australia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/plastic.rs: -------------------------------------------------------------------------------- 1 | //! Calculate various plastic zone properties. 2 | 3 | use std::f64::consts::PI; 4 | use std::ops::{Add, Sub}; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct ZoneWidth { 8 | pub plane_stress: f64, 9 | pub plane_strain: f64, 10 | } 11 | 12 | impl Add for ZoneWidth { 13 | type Output = ZoneWidth; 14 | 15 | fn add(self, offset: f64) -> ZoneWidth { 16 | ZoneWidth { 17 | plane_stress: self.plane_stress + offset, 18 | plane_strain: self.plane_strain + offset, 19 | } 20 | } 21 | } 22 | 23 | impl Sub for ZoneWidth { 24 | type Output = ZoneWidth; 25 | 26 | fn sub(self, offset: f64) -> ZoneWidth { 27 | ZoneWidth { 28 | plane_stress: self.plane_stress - offset, 29 | plane_strain: self.plane_strain - offset, 30 | } 31 | } 32 | } 33 | 34 | /// Calculate the size of the plastic zone for (plane stress, plane strain) 35 | /// Ref. Anderson P. 485 36 | /// Ref. Tada 1973 P. 1.17 37 | pub fn zone_size(kmax: f64, sigma_yield: f64) -> ZoneWidth { 38 | // alpha is a constraint factor 39 | let alpha_plane_stress = 2.0; 40 | let alpha_plane_strain = 6.0; 41 | 42 | // radius of onset of yield with adjustment of the shape of the yield zone 43 | let radius_yield = |alpha: f64| (1.0 / (alpha * PI)) * (kmax / sigma_yield).powi(2); 44 | 45 | // the plastic zone width is approximately 2 * radius_yield 46 | ZoneWidth { 47 | plane_stress: 2.0 * radius_yield(alpha_plane_stress), 48 | plane_strain: 2.0 * radius_yield(alpha_plane_strain), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /doc/summary.md: -------------------------------------------------------------------------------- 1 | # Getting information about the loading sequence 2 | Here is an example of the `--summary` option used to obtain 3 | information about the sequence `seq2.txt` and the cycles it 4 | contains. 5 | 6 | ``` 7 | ./easigro -q examples/seq2.txt --summary 8 | 9 | # easiGro: version 0.0.0 10 | # 11 | 12 | Sequence Summary 13 | ---------------- 14 | 15 | Source: examples/seq2.txt 16 | Sequence mods: SequenceModifiers { 17 | cap_max: None, 18 | cap_min: None, 19 | remove_bigger: None, 20 | remove_smaller: None, 21 | cycles: false, 22 | reorder: false, 23 | turning_points: false, 24 | outfile: None 25 | } 26 | Length: 1340 27 | Maximum: 1.0000e0 at line 3 28 | Number of times maximum occurs: 160 29 | Minimum: 0.0000e0 at line 0 30 | Number of times minimum occurs: 160 31 | Mean: 5.0000e-1 32 | Number of points >= 0: 1340 33 | Number of points < 0: 0 34 | Number of non turning points: 0 35 | Sequence: [0.0 0.9 0.1 1.0 0.0 ... 0.75 0.25 0.75 0.25 0.75] 36 | 37 | 38 | Cycle Summary 39 | ------------- 40 | 41 | Source: (Obtained from sequence using 'Rainflow' method) 42 | Cycle mods: CycleModifiers { 43 | cap_max: None, 44 | cap_min: None, 45 | remove_smaller: None, 46 | remove_bigger: None, 47 | remove_region: None, 48 | outfile: None 49 | } 50 | Number of closed cycles: 667 51 | Number of unclosed turning points: 6 52 | Largest range: 1.0000e0, with valley of 0 at line 4, peak of 1 at line 3 53 | Smallest range: 5.0000e-1 with valley of 0.25 at line 160, peak of 0.75 at line 161 54 | Mean range: 6.9070e-1 55 | Maximum R: 3.3333e-1 56 | Minimum R: 0.0000e0 57 | Mean R: 2.0025e-1 58 | ``` 59 | -------------------------------------------------------------------------------- /src/tag.rs: -------------------------------------------------------------------------------- 1 | //! Useful structure for keeping track of the original positions the 2 | //! values come from in a sequence. 3 | 4 | use std::cmp::Ordering; 5 | use cycle; 6 | 7 | #[derive(Debug, Clone, Copy)] 8 | pub struct Tag { 9 | pub value: f64, 10 | pub index: usize, 11 | } 12 | 13 | #[allow(dead_code)] 14 | fn remove_cycle_tags(cycles: &[cycle::Cycle]) -> Vec> { 15 | cycles 16 | .iter() 17 | .map(|cycle| cycle::Cycle::new(cycle.max.value, cycle.min.value)) 18 | .collect::>() 19 | } 20 | 21 | impl Tag { 22 | pub fn new(v: f64, i: usize) -> Tag { 23 | Tag { value: v, index: i } 24 | } 25 | 26 | pub fn from(v: &[f64]) -> Vec { 27 | let mut result = Vec::new(); 28 | for (i, value) in v.iter().enumerate() { 29 | result.push(Tag { 30 | value: *value, 31 | index: i, 32 | }); 33 | } 34 | result 35 | } 36 | } 37 | 38 | impl PartialEq for Tag { 39 | fn eq(&self, other: &Tag) -> bool { 40 | self.value == other.value 41 | } 42 | } 43 | 44 | impl PartialOrd for Tag { 45 | fn partial_cmp(&self, other: &Tag) -> Option { 46 | (self.value).partial_cmp(&other.value) 47 | } 48 | fn lt(&self, other: &Tag) -> bool { 49 | self.value < other.value 50 | } 51 | fn le(&self, other: &Tag) -> bool { 52 | self.value <= other.value 53 | } 54 | fn gt(&self, other: &Tag) -> bool { 55 | self.value > other.value 56 | } 57 | fn ge(&self, other: &Tag) -> bool { 58 | self.value >= other.value 59 | } 60 | } 61 | 62 | #[test] 63 | fn test_sort() { 64 | let t1 = Tag::new(1.0, 0); 65 | let t2 = Tag::new(-3.0, 1); 66 | let t3 = Tag::new(5.0, 2); 67 | 68 | let _t = vec![t1, t2, t3]; 69 | 70 | assert_eq!(t1 > t2, true) 71 | } 72 | -------------------------------------------------------------------------------- /doc/sequences.md: -------------------------------------------------------------------------------- 1 | The 'data/sequences' directory contains simple loading sequences which 2 | can be used for testing easigrow. They have been previously used for 3 | exploring fatigue crack growth effects which are described in the two 4 | papers below. 5 | 6 | In summary, the paper on rainflow counting showed that there was 7 | essentially no memory effect. So while reordering a sequence using 8 | rainflow counting works for crack growth, any process that reproduces 9 | the same crack extension will result in the same growth, based on 10 | crack extension occuring on the incresing loading part of the cycle 11 | e.g. if we have 10 repeated cycles going from 0.0 to 1.0. We will get 12 | the same growth if we have 10 cycles of 0--0.5 followed by 10 cycles 13 | of 0.5--1.0 . It seems that the stress ratio effect is more signficant 14 | for short periods of growth than is indicated from constant amplitude 15 | testing at higher stress ratios. 16 | 17 | Secondly, the measurements on crack closure showed that the variations 18 | in crack grwoth rate at different stress ratios did not correlate with 19 | measurements of crack closure. Crack closure was very slow to build 20 | up, occuring over a distance of millimeters, whereas the change in 21 | growth rate due to different mean stress occured within a few cycles. 22 | 23 | The papers are: 24 | 25 | ''' 26 | @Article{white14, 27 | author = {Paul White and David S. Mongru}, 28 | title = {Fractographic study on the use of rainflow counting for small and long 29 | cracks in {AA7050}}, 30 | journal = {Advanced Materials Research}, 31 | year = 2014, 32 | volume = {891--892}, 33 | pages = {687--692} 34 | } 35 | 36 | @Article{white18, 37 | author = {P. D. White and S. A. Barter and N. Medhekar}, 38 | title = {Comparison of fractographic and strain gauge measurement of 39 | load ratio effects under variable amplitude loading}, 40 | journal = {International Journal of Fatigue}, 41 | volume = 112, 42 | pages = {240--252}, 43 | month = {July}, 44 | year = 2018 45 | } 46 | ''' 47 | -------------------------------------------------------------------------------- /src/compact.rs: -------------------------------------------------------------------------------- 1 | //! Calculations for a compact tension coupon. 2 | //! 3 | //! Equations are take from the reference: 4 | //! Back-face strain compliance relation for compact specimens for wide 5 | //! range in crack lengths 6 | //! J.C. Newman Jr., Y. Yamada, M.A. James 7 | //! Engineering Fracture Mechanics 78 (2011) 2707–2711 8 | 9 | #[allow(dead_code)] 10 | /// Calculate the crack length from backface strain measurements. 11 | fn cracklength_from_backface_strain(strain: f64, youngs_modulus: f64, depth: f64, load: f64) -> f64{ 12 | let a = youngs_modulus * strain * depth / load; 13 | let u = 1.0 /(a.sqrt() + 1.0); 14 | 15 | let a0 = 1.0033; 16 | let a1 = -2.35; 17 | let a2 = 1.3694; 18 | let a3 = -15.294; 19 | let a4 = 63.182; 20 | let a5 = -74.42; 21 | 22 | let a_on_d = a0 + a1 * u + a2 * u.powi(2) + a3 * u.powi(3) + a4 * u.powi(4) + a5 * u.powi(5); 23 | 24 | a_on_d * depth 25 | } 26 | 27 | 28 | /// Calculate the back face strain knowing the crack size. 29 | pub fn backface_strain(a_on_d: f64, load: f64, youngs_modulus: f64, depth:f64, width:f64) -> f64 { 30 | let compliance = (1.41 - 1.462 * a_on_d + 20.45 * a_on_d.powi(2) 31 | - 26.83 * a_on_d.powi(3) + 11.45 * a_on_d.powi(4)) / (1.0 - a_on_d).powi(2); 32 | 33 | let strain = compliance * load /(youngs_modulus * depth * width); 34 | 35 | strain 36 | } 37 | 38 | 39 | /// Displacement of a front face clip gauge. 40 | /// 41 | /// from ASTM 647 42 | pub fn clipgauge_displacement(a_on_d: f64, load: f64, youngs_modulus: f64, depth:f64, width:f64) -> f64 { 43 | // this equation needs to be inverted to solve for u 44 | let v = 0.0; // clipgauge displacement 45 | let u = ((youngs_modulus * v * width / load).sqrt() + 1).recip(); 46 | 47 | let a_on_d = 1.0010 - 4.6695 * u + 18.460 * u.powi(2) 48 | - 236.82 * u.powi(3) + 1214.9 * u.powi(4) - 2143.6 * u.powi(5); 49 | 50 | a_on_d 51 | } 52 | 53 | #[test] 54 | /// From figure 2 in the paper a ratio of a/w = 0.45 has a compliance of 10. 55 | /// compliance is E(epsilon W) B/P 56 | fn check_compliance_backface() { 57 | let strain = backface_strain(1.0, 1.0, 1.0, 1.0, 1.0); 58 | 59 | println!("compliance check {}", strain - 10.0); 60 | } 61 | -------------------------------------------------------------------------------- /tests/test-comparison: -------------------------------------------------------------------------------- 1 | # Here are some comparisons of easigrow predictions with rainflow test data 2 | coupon sequence easigrow test 3 | # short crack dogbone coupon 4 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq2.txt -s 300 --beta qcct-mcdonald07 5 | 0 2 228.0915 135.5 6 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq3.txt -s 240 --beta qcct-mcdonald07 7 | 0 3 482.9070 173.3 8 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq4.txt -s 300 --beta qcct-mcdonald07 9 | 0 4 123.0112 95.6 10 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq5.txt -s 300 --beta qcct-mcdonald07 11 | 0 5 339.7211 237.1 12 | # Long crack middle crack tension coupon 13 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq2.txt -s 75 --beta ct-koiter65 --astart 6e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 14 | 1 2 83.0315 101.4 15 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq3.txt -s 60 --beta ct-koiter65 --astart 6e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 16 | 1 3 170.6177 277.6 17 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq4.txt -s 75 --beta ct-koiter65 --astart 6e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 18 | 1 4 42.3279 85.0 19 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/rainflow-seq5.txt -s 75 --beta ct-koiter65 --astart 6e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 20 | 1 5 116.0758 255.4 21 | # 22 | # Here are some more comparisons with crack closure tests 23 | # see @Article{white18, 24 | # author = {P. D. White and S. A. Barter and N. Medhekar}, 25 | # title = {Comparison of fractographic and strain gauge measurement of load ratio effects under variable amplitude loading}, 26 | # journal = {International Journal of Fatigue}, 27 | # volume = 112, 28 | # pages = {240--252}, 29 | # month = {July}, 30 | # year = 2018} 31 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/closure-seq1.txt -s 5000e-6 --beta compact-tada73 --astart 11.45e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 --sideways 12e-3 32 | 2 7 185.1172 330.5 33 | 2 7 185.1172 347.11 34 | 2 7 185.1172 312.65 35 | # easigrow --dadn white:barter14-aa7050t7451 -q data/sequences/closure-seq1.txt -s 4000e-6 --beta compact-tada73 --astart 11.45e-3 -e 0.1 -n 10 --output_vars block,a,a/d --forward 48e-3 --sideways 12e-3 36 | 2 8 223.3494 388.66 37 | 2 8 223.3494 342.68 38 | -------------------------------------------------------------------------------- /src/description.md: -------------------------------------------------------------------------------- 1 | # easiGRO 2 | 3 | Calculates fatigue crack growth rates and finds the 4 | optimum parameters of a crack growth model to match predictions 5 | with measurements. 6 | 7 | The distances to the free edges on the coupon are specified 8 | relative to the origin of the crack. i.e. they are not the coupon 9 | dimensions. The crack depth 'a' is in the `forward` 10 | direction. The crack length 'c' is in the `sideways` 11 | direction. If the crack is not symmetrically located within the 12 | coupon, use the distance to the nearest free edge. 13 | 14 | a) |<-2c->| b) || 15 | ________________ ___ ________________ ___ 16 | | \ / ^ | ^ | | ^ | ^ 17 | | \___/ v a | forward |_ / v a | forward 18 | | | | | 19 | |________________| _v_ |________________| _v_ 20 | 21 | |sideways| |<-- sideways -->| 22 | 23 | Figure: Cross section of a coupon showing crtical dimension names 24 | with a) semi-eliptical crack, and b) corner crack. 25 | 26 | Example 27 | ======== 28 | 29 | ``` 30 | easigro -q ft55.seq -s 300 -r -b seft-newman84 --cycle_method rainflow -d walker:default -a 10e-6 -e 5e-3 -o block,a -n 10 31 | ``` 32 | 33 | This means: 34 | 35 | Read in the sequence with the filename 'ft55.seq' [-q ft55.seq] 36 | scaling by a factor of 300 MPa [-s 300] and reorder the sequence [-r] 37 | to close all cycles. Use the beta model for a 'semi-elliptical surface 38 | crack in a finite plate in tension' by Newman and Raju [-b 39 | seft-newman84] and calculate the crack size by summing up the growth 40 | increment for each rainflow cycle [--cycle_method rainflow] using the 41 | Walker da/dN equation with default material parameters [-d 42 | walker:default]. Starting at an initial crack size 10 um [-a 10e-6] 43 | grow the crack until a final size 5 mm [-e 5e-3], writing out the 44 | variables 'block' and 'a' [-o block,a] every 10 blocks [-n 10]. 45 | 46 | # How the program works 47 | 48 | Think of the program flow as 49 | 50 | 1. Read in data 51 | 2. Filter the sequence (turning point, rainflow, risefall, deadband etc.) and convert to cycles 52 | 3. Filter the list of cycles 53 | 4. If required, optimise any matching parameters 54 | 5. Perform a crack growth calculation 55 | 6. Write out requested output 56 | 57 | -------------------------------------------------------------------------------- /src/vector.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | use std::ops::{Add, Div, Mul, Sub}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct MVector { 7 | pub m: Vec, 8 | } 9 | 10 | impl MVector { 11 | fn new(a: Vec) -> MVector { 12 | MVector { m: a } 13 | } 14 | // compatability with nalgebra 15 | pub fn from_row_slice(n: usize, a: &[f64]) -> MVector { 16 | let mut m = Vec::new(); 17 | for value in a.iter().take(n) { 18 | m.push(*value); 19 | } 20 | MVector::new(m) 21 | } 22 | 23 | pub fn as_slice(&self) -> &[f64] { 24 | self.m.as_slice() 25 | } 26 | 27 | pub fn len(&self) -> usize { 28 | self.m.len() 29 | } 30 | } 31 | 32 | // simplified matrix multiplication to avoid using nalgebra 33 | impl Add> for MVector { 34 | type Output = MVector; 35 | fn add(self, rhs: MVector) -> MVector { 36 | let mut ans = Vec::new(); 37 | for (a, b) in self.m.iter().zip(rhs.m) { 38 | ans.push(a + b); 39 | } 40 | MVector::new(ans) 41 | } 42 | } 43 | 44 | impl Add for MVector { 45 | type Output = MVector; 46 | fn add(self, rhs: f64) -> MVector { 47 | let mut ans = Vec::new(); 48 | for a in self.m { 49 | ans.push(a + rhs); 50 | } 51 | MVector::new(ans) 52 | } 53 | } 54 | 55 | impl Sub> for MVector { 56 | type Output = MVector; 57 | fn sub(self, rhs: MVector) -> MVector { 58 | let mut ans = Vec::new(); 59 | for (a, b) in self.m.iter().zip(rhs.m) { 60 | ans.push(a - b); 61 | } 62 | MVector::new(ans) 63 | } 64 | } 65 | 66 | impl Mul for MVector { 67 | type Output = MVector; 68 | fn mul(self, rhs: f64) -> MVector { 69 | let mut ans = Vec::new(); 70 | for a in self.m { 71 | ans.push(a * rhs); 72 | } 73 | MVector::new(ans) 74 | } 75 | } 76 | 77 | impl Div for MVector { 78 | type Output = MVector; 79 | fn div(self, rhs: f64) -> MVector { 80 | let mut ans = Vec::new(); 81 | for a in self.m { 82 | ans.push(a / rhs); 83 | } 84 | MVector::new(ans) 85 | } 86 | } 87 | 88 | impl Mul> for f64 { 89 | type Output = MVector; 90 | fn mul(self, rhs: MVector) -> MVector { 91 | let mut ans = Vec::new(); 92 | for a in rhs.m { 93 | ans.push(a * self); 94 | } 95 | MVector::new(ans) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/sweep.rs: -------------------------------------------------------------------------------- 1 | use options; 2 | use factors; 3 | use optimise; 4 | use numbers::NonNan; 5 | use std::{process, f64}; 6 | use self::rayon::prelude::*; 7 | 8 | extern crate rayon; 9 | 10 | /// Perform a brute force sweep over all permutations of the parameters. 11 | /// 12 | /// Calculates the error function for each permutation of the 13 | /// factors. This function can be used as a brute force optimisation to search 14 | /// a space for the best fit. Or, it could be used for a one off error 15 | /// function evaluation for all of the crack files that are to be matched. 16 | pub fn sweep(options: &options::EasiOptions, params: &mut [f64]) -> f64 { 17 | println!("Performing a brute force sweep. This may take some time..."); 18 | let sweep_factors = factors::permutations(&options.optimise.sweep, params.len()); 19 | println!("Sweep: there are {} combinations", sweep_factors.len()); 20 | println!("Sweep: parameters {:?}", options.params); 21 | 22 | if options.verbosity == options::Verbosity::Verbose { 23 | for factor in &sweep_factors { 24 | println!("Sweep factors {:?}", factor); 25 | } 26 | } 27 | if options.optimise.file == "" { 28 | println!( 29 | "Error: you need an optimisation file to perform a sweep in order to calculate errors" 30 | ); 31 | process::exit(1); 32 | } 33 | 34 | let all_options = optimise::get_all_options(options, &options.optimise.file); 35 | let mut results = Vec::with_capacity(sweep_factors.len()); 36 | sweep_factors 37 | .par_iter() 38 | .map(|normalised_factors| { 39 | let scaled_factors = normalised_factors 40 | .iter() 41 | .zip(options.params.iter()) 42 | .map(|(&f, &p)| f * p) 43 | .collect::>(); 44 | let single_error = optimise::prediction_error(&scaled_factors, &all_options); 45 | let total_single_error = single_error 46 | .iter() 47 | .map(|x| optimise::rms(x).powi(2)) 48 | .sum::() 49 | .sqrt(); 50 | (normalised_factors, total_single_error) 51 | }) 52 | .collect_into_vec(&mut results); 53 | 54 | for &(f, r) in &results { 55 | println!("{:?} {:?}", f, r); 56 | } 57 | 58 | // print out the results for the smallest error 59 | let (best_normalised_factors, smallest_error) = results 60 | .into_iter() 61 | .min_by_key(|&(_factor, error)| NonNan::new(error.abs())) 62 | .unwrap(); 63 | 64 | println!("Best result from sweep:"); 65 | println!(" Total Error: {:?}", smallest_error); 66 | println!(" Normalised factors: {:?}", best_normalised_factors); 67 | 68 | // copy back the best parameters 69 | let n = params.len(); 70 | params.clone_from_slice(&best_normalised_factors[..n]); 71 | 72 | smallest_error 73 | } 74 | -------------------------------------------------------------------------------- /doc/grow.md: -------------------------------------------------------------------------------- 1 | # Crack growth calculation 2 | 3 | Here is an example of a simple crack growth calculation for a 4 | compact tension coupon. The compact tension geometry factor uses the 5 | applied load on the coupon rather than the far-field stress, so for 6 | this example the scaling factor is 5000 N and because consistent units 7 | are MN this becomes 5000E-6 MN. The forward distance of 50 mm 8 | represents the distance from the centre-line of the pin attachment 9 | holes to the free edge but the sideways distance is actually the full 10 | width of the coupon of 12.5 mm. The initial starting distance of 15 mm 11 | is the length of the crack from the centre-line of the pin 12 | attachments. All distance units are given in m. 13 | 14 | ``` 15 | ./easigrow -s 5000e-6 -b compact-tada73 -o block,a,k -a 0.015 -e 0.05 16 | -q sequences/seq2.txt --forward=0.05 --sideways=0.0125 -n 10 17 | 18 | # easigrow: version 0.0.0 19 | # 20 | # Options: 21 | # a: [0.015] 22 | # a_limit: [0.05] 23 | # block_limit: 1000.0 24 | # params: [] 25 | # output_vars: ["block", "a", "k"] 26 | # output_every: 10 27 | # output_lines: [1] 28 | # scale: 0.005 29 | # beta: "compact-tada73" 30 | component: Component { forward: 0.05, sideways: 0.0125, radius: inf, material: Properties { yield_stress: 450.0, k1c: 33.0, youngs_modulus: 71000.0 } } 31 | # seq_infile: "../check/sequences/seq1.txt" 32 | # seq_mods: SequenceModifiers { cap_max: None, cap_min: None, remove_bigger: None, remove_smaller: None, cycles: false, reorder: false, turning_points: false, outfile: None } 33 | # cycle_method: Rainflow 34 | # cycle_infile: "" 35 | # cycle_mods: CycleModifiers { cap_max: None, cap_min: None, remove_smaller: None, remove_bigger: None, remove_region: None, outfile: None } 36 | # dadn: "white:barter14-aa7050t7451" 37 | # No parameters given, obtaining from material library for white:barter14-aa7050t7451 38 | # da/dn equation: White { a: 0.25481858, b: 1.10247048, c: 4.35831677, d: 23.08586582, e: 0.03420171, f: 0.4717843, kic: 31.54, cite: "unknown", units: "m" } 39 | # da/dN (m) = exp[(2.5481858e-1 * ΔKeff^3 - 1.10247048e0 * ΔKeff^2 + 4.35831677e0 * ΔKeff - 2.308586582e1) 40 | # + (dkic - ΔK)^-0.4717843] [White14:unknown] 41 | # where dkic = 3.154e1 (1 - R) and ΔKeff = ΔK / (1 - R)^0.03420171 42 | block a k 43 | 0.0000 1.500000e-2 0.0000 44 | 10.0000 1.543065e-2 10.2813 45 | 20.0000 1.589049e-2 10.5276 46 | 30.0000 1.638381e-2 10.7977 47 | 40.0000 1.691594e-2 11.0965 48 | 50.0000 1.749375e-2 11.4305 49 | 60.0000 1.812628e-2 11.8087 50 | 70.0000 1.882580e-2 12.2439 51 | 80.0000 1.960970e-2 12.7555 52 | 90.0000 2.050391e-2 13.3739 53 | 100.0000 2.155006e-2 14.1522 54 | 110.0000 2.282240e-2 15.1927 55 | 120.0000 2.447781e-2 16.7376 56 | 130.0000 2.697975e-2 19.6222 57 | 138.4711 1.744446e-1 60.7851 58 | # Failure Event: a[0.17444463103533853] >= a_limit[0.05] 59 | # Failure Event: a[0.17444463103533853] > depth[0.05] 60 | # Failure Event: k[60.78514498903261] > k1c[33] 61 | ``` 62 | -------------------------------------------------------------------------------- /src/table_gsl.rs: -------------------------------------------------------------------------------- 1 | extern crate rgsl; // used for spline interpolation of table 2 | use log::debug; 3 | 4 | use table; 5 | 6 | /// increase the table so there is a row column for each column. 7 | pub struct PairTable { 8 | /// columns 9 | pub columns: Vec, 10 | /// rows 11 | pub rows: Vec>, 12 | /// vectors of columns 13 | pub values: Vec>, 14 | // GSL Splines 15 | splines: Vec, 16 | } 17 | 18 | impl PairTable { 19 | pub fn new(columns: Vec, rows: Vec>, values: Vec>) -> PairTable { 20 | let mut splines = Vec::new(); 21 | 22 | for i in 0..columns.len() { 23 | let spline = rgsl::Spline::new(&rgsl::InterpType::cspline(), rows[i].len()).unwrap(); 24 | spline.init(&rows[i], &values[i]); 25 | splines.push(spline); 26 | } 27 | 28 | PairTable { 29 | columns, 30 | rows, 31 | values, 32 | splines, 33 | } 34 | } 35 | 36 | /// This is a simple interpolation of a table of variables. 37 | /// 38 | /// The most signicant variable (the one that produces the largest 39 | /// change in the return variable) should be the rows (which are 40 | /// spline fitted for accuracy) whereas the slower secondary 41 | /// variable are the coluumns (which are only linearly 42 | /// interpolate). Splines are fit to the upper and lower columns 43 | /// around the desired `column` point and then interpolate to the 44 | /// row value for each column. The lower and upper interpolations 45 | /// values are linearly interpolated to the column value. 46 | /// 47 | /// If `invert` is true we swap the `values` and `rows` so that we 48 | /// are interpolating the `values` to find the `row` like 49 | /// value. This routine does not extrapolate so both the row and 50 | /// column variables are capped to be the maximum or minimum values 51 | /// in the table. 52 | pub fn interp(&self, row: f64, column: f64) -> f64 { 53 | debug!("New interpolation row: {}, column: {}", row, column); 54 | let col_limited = column 55 | .max(self.columns[0]) 56 | .min(self.columns[(self.columns.len() - 1)]); 57 | let col_low = table::nearest(col_limited, &self.columns); 58 | let row_limited = row.max(self.rows[col_low][0]) 59 | .min(self.rows[col_low][(self.rows[col_low].len() - 1)]); 60 | debug!("Col limited {:?}, row limited {:?}", col_limited, row_limited ); 61 | debug!("col {}", col_low); 62 | let mut accel = rgsl::InterpAccel::new(); 63 | 64 | debug!("Columns: {:?}", self.columns); 65 | debug!("interp for {} using col {}", row, col_low); 66 | let row_s = self.splines[col_low].eval(row_limited, &mut accel); 67 | debug!("done interp row_s {}", row_s); 68 | 69 | // simple linear interpolation between the lower and upper columns 70 | if self.columns.len() > 1 { 71 | debug!("End interpolation"); 72 | let mut accel = rgsl::InterpAccel::new(); 73 | let row_limited = row.max(self.rows[col_low + 1][0]) 74 | .min(self.rows[col_low + 1][(self.rows[col_low + 1].len() - 1)]); 75 | let row_e = self.splines[col_low + 1].eval(row_limited, &mut accel); 76 | debug!("done interp row_e {}", row_e); 77 | let interp = ((column - self.columns[col_low]) 78 | / (self.columns[col_low + 1] - self.columns[col_low])) 79 | * (row_e - row_s) + row_s; 80 | debug!("interp result: {}", interp); 81 | interp 82 | } else { 83 | row_s 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /doc/optimisation.md: -------------------------------------------------------------------------------- 1 | # Optimisation 2 | To calculate the optimium coefficients that produces crack growth that matches 3 | existing crack growth data we need to supply the following information: 4 | 5 | 1. a crack growth model corresponding to each crack measurement file 6 | that including beta, stress, start and end cracks size, dadn model 7 | 2. loading sequences used for the crack measurements 8 | 9 | Each line in the optimisation file is an easigrow command line that will 10 | generate a crack growth curve along with a measurement file of crack 11 | growth data that it tries to match. 12 | 13 | ``` 14 | 15 | 16 | ``` 17 | 18 | Here is a sample listing of crack growth measurements obtained from a microscope. 19 | 20 | ``` 21 | Item X (mm) Y (mm) Z (mm) Radius (mm) 22 | 1.00 0.0000 0.0000 -0.0000 0.0000 23 | 2.00 0.1280 0.1638 0.1991 0.2079 24 | 3.00 -0.0128 6.0006 0.0009 6.0007 25 | 4.00 -3.7212 6.2297 -0.0620 7.2565 26 | 5.00 -3.7212 6.2297 -0.0620 7.2565 27 | 6.00 -3.7572 6.2297 -0.0666 7.2750 28 | 7.00 -3.7910 6.2280 -0.0670 7.2911 29 | 8.00 -3.6810 5.7518 -0.1105 6.8288 30 | 9.00 -3.6810 5.7518 -0.1105 6.8288 31 | 10.00 -3.7184 5.7518 -0.0871 6.8490 32 | ``` 33 | 34 | Note, these measurements are taken at the same point within a block or 35 | feature on the fracture surface. In this case, because we are only 36 | dealing with blocks, the exact load line the measurements were made at 37 | is not important. Unless otherwise specified, the program will assume 38 | it is the first load line. Contiguous measurements are made until the 39 | blocks can no longer be identified. We indicate that a measurement is 40 | not contiguous with the previous block and is the start of a new run 41 | by pressing the measurement button twice. Here measurements 4 and 5, 42 | and 8 and 9 are both the same indicating the start of a new run. 43 | 44 | This measurement file is turned into an input file that looks like 45 | ```bash 46 | 0.2079 47 | 48 | 6.0007 49 | 50 | 7.2565 51 | 7.2750 52 | 7.2911 53 | 54 | 6.8288 55 | 6.8490 56 | ``` 57 | 58 | An example of the output from an optimisation run is shown below. The 59 | output shows how the optimisation is doing. Here we have used the 60 | Walker equation which contains three variables which are optimised, 61 | they are [0.00000001, 3, 0.5]. We also include the crack growth from 4 62 | coupons, each with an associated crack growth file. The error term is 63 | calculated for each of the files and compared with the 64 | easigrow predictions using the initial set of parameters. In this 65 | case the initial parameters are the default values which are not very 66 | good. 67 | 68 | There are a number of different optimisation methds but the simplest 69 | is Nelder optimsiation method which is used for this example. At the 70 | start of a Nelder optimisation the algorithm adds a small increment to 71 | each of the optimisation parameters in turn to determine which direction is 72 | best to begin the optimisation. Then it will head off in 73 | the most favourable direction adjusting the other parameters looking 74 | for directions that result in some reduction in the overall 75 | error. When the level of improvement in each step falls below the 76 | convergence threshold it will terminate the optimisation with a message saying it has 77 | converged. 78 | 79 | The program may also terminate if it has exceeded the maximum number 80 | of iterations. The default value for the maximum number of iterations 81 | in an optimisation is 100. This can be overridden on the command line, 82 | as we have done here, increasing the maximum iteration limit to 83 | 1000. The initial error terms for each of the crack growth files are 84 | all in the region of 100--200 i.e. not a good prediction. Eventually, 85 | the solution converges and we have an *optimised* answer where the 86 | error between the optimised predictions and the measured crack growth 87 | are of the order of 20--30. A significant improvement. The final 88 | optimised parameters are [1.23759e-9, 2.0023, 1.8756]. 89 | 90 | Inside the program, the optimisation is performed on a 91 | non-dimensionalised version of the parameters, where the parameters 92 | are normalised based on their starting values. This makes the scaling 93 | of the parameters much closer to each other, which is better for most 94 | optimisation routines. The resultant normalised factors are [0.12376, 95 | 0.66743, 3.7511]. 96 | 97 | ``` 98 | easigro --optimise optim --opt_max 1000 --opt_method Nelder -d walker:default 99 | 100 | ``` 101 | 102 | -------------------------------------------------------------------------------- /src/table_bspline.rs: -------------------------------------------------------------------------------- 1 | /// Tables with spline interpolation using the bspline library 2 | 3 | use log::debug; 4 | 5 | use bspline; 6 | use table::nearest; 7 | 8 | pub struct PairTable { 9 | /// columns 10 | pub columns: Vec, 11 | /// rows 12 | pub rows: Vec>, 13 | /// vectors of columns 14 | pub values: Vec>, 15 | /// Splines 16 | splines: Vec>, 17 | } 18 | 19 | impl PairTable { 20 | pub fn new(columns: Vec, 21 | rows: Vec>, 22 | values: Vec>) -> PairTable { 23 | let mut splines = Vec::new(); 24 | let degree = 2; 25 | debug!("PairTable::new()"); 26 | debug!("columns: {:?}", columns); 27 | debug!("rows: {:?}", rows); 28 | debug!("values: {:?}", values); 29 | 30 | // make a spline for every set of row and column 31 | for i in 0..columns.len() { 32 | let mut knots = Vec::new(); 33 | let start = rows[i][0]; 34 | knots.push(start as f32); 35 | knots.push(start as f32); 36 | // let mut t = rows[i].clone() ; 37 | let mut t = rows[i].iter().map(|&e| e as f32).collect() ; 38 | knots.append(&mut t); 39 | let end = knots[knots.len() - 1]; 40 | knots.push(end); 41 | 42 | let mut points = Vec::new(); 43 | let mut v = values[i].iter().map(|&e| e as f32).collect(); 44 | 45 | points.append(&mut v); 46 | 47 | debug!("Knots are: {:?}", knots); 48 | let spline = bspline::BSpline::new(degree, points, knots); 49 | splines.push(spline); 50 | } 51 | 52 | PairTable { 53 | columns, 54 | rows, 55 | values, 56 | splines, 57 | } 58 | } 59 | 60 | /// This is a simple interpolation of a table of variables. 61 | /// 62 | /// The most signicant variable (the one that produces the largest 63 | /// change in the return variable) should be the rows (which are 64 | /// spline fitted for accuracy) whereas the slower secondary 65 | /// variable are the coluumns (which are only linearly 66 | /// interpolate). Splines are fit to the upper and lower columns 67 | /// around the desired `column` point and then interpolate to the 68 | /// row value for each column. The lower and upper interpolations 69 | /// values are linearly interpolated to the column value. 70 | /// 71 | /// If `invert` is true we swap the `values` and `rows` so that we 72 | /// are interpolating the `values` to find the `row` like 73 | /// value. This routine does not extrapolate so both the row and 74 | /// column variables are capped to be the maximum or minimum values 75 | /// in the table. 76 | pub fn interp(&self, row: f64, column: f64) -> f64 { 77 | debug!("New interpolation row: {}, column: {}", row, column); 78 | let col_limited = column 79 | .max(self.columns[0]) 80 | .min(self.columns[(self.columns.len() - 1)]); 81 | let col_low = nearest(col_limited, &self.columns); 82 | let row_limited = row.max(self.rows[col_low][0]) 83 | .min(self.rows[col_low][(self.rows[col_low].len() - 1)]); 84 | debug!("Col limited {:?}, row limited {:?}", col_limited, row_limited ); 85 | debug!("col {}", col_low); 86 | 87 | debug!("Columns: {:?}", self.columns); 88 | debug!("interp for {} using col {}", row, col_low); 89 | debug!("knot domain {:?}", self.splines[col_low].knot_domain()); 90 | 91 | let row_s = f64::from(self.splines[col_low].point(row_limited as f32)); 92 | debug!("done interp row_s {}", row_s); 93 | debug!("No. of columns {}", self.columns.len()); 94 | 95 | // simple linear interpolation between the lower and upper columns 96 | if self.columns.len() > 1 { 97 | debug!("End interpolation"); 98 | let row_limited = row.max(self.rows[col_low + 1][0]) 99 | .min(self.rows[col_low + 1][(self.rows[col_low + 1].len() - 1)]); 100 | let row_e = f64::from(self.splines[col_low + 1].point(row_limited as f32)); 101 | debug!("done interp row_e {:?}", row_e); 102 | let interp = ((column - self.columns[col_low]) 103 | / (self.columns[col_low + 1] - self.columns[col_low])) 104 | * (row_e - row_s) + row_s; 105 | debug!("interp result: {}", interp); 106 | interp 107 | } else { 108 | row_s as f64 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easigrow 2 | Create models of fatigue crack growth 3 | 4 | Easigrow is a fatigue crack growth program that specialises in 5 | calculating model coefficients that best fit crack growth data. 6 | 7 | Easigrow is based on the Australian Defence Science and Technology report: 8 | A guide to the program easigro for generating optimised fatigue crack 9 | growth models, Paul White, DST-Group-TR-3566, Feb. 2019. 10 | 11 | Note the addition of 'w' here for the code on Github. 12 | The version number has also been reset to 0.0.0. 13 | 14 | ## Features 15 | 16 | - Calculates the crack growth size 17 | - Choice of beta models, material properties and crack growth equations 18 | - Optimises model coefficients to match data 19 | - Generates a pseudo image of the crack growth patterns 20 | 21 | ## Getting started 22 | 23 | Easigrow provides both a command line tool for running simulations 24 | and a Rust library for writing your own algorithms using the 25 | pre-existing building blocks. 26 | 27 | Here is a simple example of growing a crack from the command line: 28 | 29 | ``` 30 | easigrow -q data/rainflow-seq2.txt -s 300 -n 100 31 | 32 | # easigrow: version 0.0.0 33 | # 34 | # Options: 35 | # a: [0.00001, 0.00001] 36 | ... 37 | block a 38 | 0.0000 1.000000e-5 39 | 100.0000 2.189452e-5 40 | 200.0000 5.234739e-4 41 | 204.0975 7.473615e-4 42 | # Failure Event: a[0.0007473615299278992, 0.0010002610857908832] >= a_limit[0.001, 0.001] 43 | ``` 44 | 45 | ### Documentation 46 | 47 | The following documentation contains most of what is in the above report: 48 | 49 | * [help](doc/help.md) `easigrow --help` command line options 50 | * [list](doc/list.md) `easigrow --list` output variables, beta models, da/dN data 51 | * [dadn](doc/dadn.md) dadn equations used (incomplete because of markdown) 52 | * [grow](doc/grow.md) grow the crack 53 | * [optimisation](doc/optimisation.md) How to optimsise a model 54 | * [fracto](doc/fracto.md) generate a pseudo-fracto image 55 | * [sequences](doc/sequences.md) Supplied loading sequences 56 | * [summary](doc/summary.md) Summarise a sequence 57 | 58 | You can also run `Cargo doc` to generate documentation for all the library public functions and use Easigrow as a library inside your own code. 59 | 60 | ### Installation as a command line tool 61 | 62 | You will need a stable Rust compiler, [grab one][Rust] if you do not have one 63 | yet. Then, you can download the code, build it and install it by running: 64 | 65 | ```bash 66 | cargo install --git https://github.com/needsglasses/easigrow 67 | ``` 68 | 69 | This will produce the `easigrow` binary in `~/.cargo/bin`. 70 | 71 | ### Usage as a library 72 | 73 | You can add Easigrow as a dependency in your project's `Cargo.toml`: 74 | 75 | ```toml 76 | [dependencies] 77 | easigrow = {git = "https://github.com/needsglasses/easigrow"} 78 | ``` 79 | 80 | ## Contributing 81 | 82 | Contributions to Easigrow are welcome. There are several ways to help: 83 | improving the documentation; testing the code on your systems to find 84 | bugs; adding new algorithms or data; providing feature requests. 85 | 86 | See the [AUTHORS](AUTHORS) file for a list of contributors to the code. 87 | 88 | ## License 89 | 90 | This software is licensed under the MIT license, see the 91 | [LICENSE-MIT](LICENSE-MIT) file for legal text. 92 | 93 | Unless you explicitly state otherwise, any contribution intentionally submitted 94 | for inclusion in the work by you, shall be licensed under the same MIT license, 95 | without any additional terms or conditions. 96 | 97 | [Rust]: https://www.rust-lang.org/downloads.html 98 | [issues]: https://github.com/needsglasses/easigrow/issues/new 99 | [user_manual]: http://needsglasses.github.io/easigrow/latest/book/ 100 | [devdoc]: http://needsglasses.github.io/easigrow/latest/easigrow/ 101 | 102 | ## Other Programs 103 | 104 | Fatigue crack growth programs 105 | 106 | * [Afgrow] USAF crack growth software for Windows 107 | * [Nasgro] NASA crack growth software with large material library 108 | * [Fastran] Crack growth using closure in Fortran 109 | 110 | and some other open source programs on Github: 111 | 112 | * [pdprop] Simulation of metal fatigue crack propagation with accounting for material memory effects in Fortran 113 | * [FCGR] Fatigue Crack Growth in Reliability using R 114 | * [metal-fatigue] Start of a fatigue library in Python with some rainflow counting routines 115 | * [fatpack]: Python fatigue routines 116 | 117 | [efatigue]: https://www.efatigue.com/ 118 | [Fastran]: https://www.researchgate.net/publication/24314111_FASTRAN-2_A_fatigue_crack_growth_structural_analysis_program 119 | [Nasgro]: https://www.swri.org/consortia/nasgro 120 | [Afgrow]: https://www.afgrow.net/ 121 | [metal-fatigue]: https://github.com/alexm2303/metal-fatigue 122 | [pdprop]: https://github.com/pdprop/pdprop 123 | [FCGR]: https://github.com/cran/FCGR 124 | [fatpack]: https://github.com/gunnstein/fatpack 125 | -------------------------------------------------------------------------------- /doc/fracto.md: -------------------------------------------------------------------------------- 1 | # Generating pseudo fractographs 2 | 3 | A fractograph is an image of the fracture surface. Here we try to 4 | generate an image of some aspects of the patterns seen on a fatigue 5 | crack under a simple repeated loading sequence. This will allow 6 | patterns to be seen on the fracture surface so that we can measure the 7 | rate of growth or use to understand the crack growth mechanisms. Do 8 | not get your hopes up about the quality of the image. 9 | 10 | In order to generate a pseudo-fractograph we need two models: 11 | 1. a model for crack growth, and 12 | 2. a model that generates an equivalent grey-scale for the loading. 13 | 14 | The image will also depend on the type of fractographic image we are 15 | trying to replicate and this depends on the type of microscope used 16 | for taking the image such as a scanning electron microscope (SEM) 17 | image or an optical microscope image. The two types of image generate 18 | contrast in different ways. 19 | 20 | Firstly, the optical image basically appears as a grey shaded image 21 | where dark regions are generated by reflecting light away from the 22 | lens and bright regions reflect more light toward the lens. Thus with 23 | an optical microscopic image, the light and dark regions correspond to 24 | the changes in path of the fracture surface. There may also be 25 | a textural component where the surface generated is more or less 26 | reflective depending on the mode of crack growth. Secondly, with an 27 | SEM, bright areas occur when more secondary electrons are released from 28 | the surface, which typically occurs where there are sharp features. 29 | 30 | The fracto image file is generated by using the 31 | `--image_outfile` to the *easigrow* command line. For example, 32 | adding `--image_outfile seq1.svg` to the command line will 33 | produce a pseudo fracto image in the file `seq1.svg`. Note, it 34 | is required to provide a file name with the suffix `svg` so 35 | that the image is in Scalable Vector Graphics (svg) format. These 36 | `svg` commands are then rendered by the viewer on the users 37 | computer. 38 | 39 | The use of `svg` format uses vectors instead of 40 | pixels which allows you to zoom in to any part of the sequence down to the 41 | resolution of one striation. 42 | 43 | Even so, it is still probably wise to not generate too many blocks in 44 | the image as this will inevitably result in a wide range of blocks 45 | sizes which will need significant amount of zooming-in to recognise 46 | the individual segments. 47 | 48 | Here is an example that produces a fracto image. 49 | 50 | ``` 51 | ./easigrow -s 5000e-6 -b compact-tada73 -o block,a,c,k -a 0.015 52 | -e .016 -q sequences/seq1.txt --forward=0.05 53 | --sideways=0.0125 -n 10 --image_outfile seq1.svg 54 | 55 | # easigrow: version 0.0.0 56 | # 57 | # Options: 58 | # a: [0.015] 59 | # a_limit: [0.016] 60 | # block_limit: 1000.0 61 | # params: [] 62 | # output_vars: ["block", "a", "c", "k"] 63 | # output_every: 10 64 | # output_lines: [1] 65 | # scale: 0.005 66 | # beta: "compact-tada73" 67 | # component: Component { forward: 0.05, sideways: 0.0125, radius: inf, material: Properties { yield_stress: 450.0, k1c: 33.0, youngs_modulus: 71000.0 } } 68 | # seq_infile: "sequences/seq1.txt" 69 | # seq_mods: SequenceModifiers { cap_max: None, cap_min: None, remove_bigger: None, remove_smaller: None, cycles: false, reorder: false, turning_points: false, outfile: None } 70 | # cycle_method: Rainflow 71 | # cycle_infile: "" 72 | # cycle_mods: CycleModifiers { cap_max: None, cap_min: None, remove_smaller: None, remove_bigger: None, remove_region: None, outfile: None } 73 | # fracto: [] 74 | # image: ImageData { file: "seq1.svg", barlength: 0.00005, xsize: 300, ysize: 8000, image: Sem } 75 | # dadn: "white:barter14-aa7050t7451" 76 | # No parameters given, obtaining from material library for white:barter14-aa7050t7451 77 | # da/dn equation: White { a: 0.25481858, b: 1.10247048, c: 4.35831677, d: 23.08586582, e: 0.03420171, f: 0.4717843, kic: 31.54, cite: "unknown", units: "m" } 78 | # da/dN (m) = exp[(2.5481858e-1 * ΔKeff^3 - 1.10247048e0 * ΔKeff^2 + 4.35831677e0 * ΔKeff - 2.308586582e1) 79 | # + (dkic - ΔK)^-0.4717843] [White14:unknown] 80 | # where dkic = 3.154e1 (1 - R) and ΔKeff = ΔK / (1 - R)^0.03420171 81 | block a c k 82 | 0.0000 1.500000e-2 1.500000e-2 0.0000 83 | 10.0000 1.543065e-2 1.543065e-2 10.2813 84 | 20.0000 1.589049e-2 1.589049e-2 10.5276 85 | 22.2703 1.600000e-2 1.600000e-2 8.8222 86 | # Failure Event: a[0.016000004096755106] >= a_limit[0.016] 87 | 88 | Making a pseudo image... 89 | frame: ImageData { file: "seq1.svg", barlength: 0.00005, xsize: 300, ysize: 8000, image: Sem } 90 | cycles in history 37814 91 | history 37814, a_max 0.015999975345001187 92 | starting crack size is 0.015000026212133203 93 | image written to file seq1.svg 94 | ``` 95 | 96 | ![image][fracto] 97 | 98 | [fracto]: https://github.com/needsglasses/examples/seq1.png "Pseudo fractograhic image of sequence 1" 99 | -------------------------------------------------------------------------------- /src/optimise_gsl.rs: -------------------------------------------------------------------------------- 1 | //! This is done by a call to the minpack Levenberg-Marquardt routine 2 | //! implemented in GSL that minimises a vector of differences of the 3 | //! growth rates. The growth rates are calculated from the target 4 | //! crack growth files by taking the differences between successive 5 | //! crack measurements in the file. 6 | 7 | use std::process; 8 | use options; 9 | use optimise; 10 | use log::{warn, error}; 11 | 12 | extern crate rgsl; 13 | 14 | #[derive(Clone)] 15 | struct Data { 16 | options: Vec, 17 | params: Vec, 18 | } 19 | 20 | // Optimise the parameters of the crack growth model to minimise the 21 | // error in matching crack growth data. Factors are the scaling 22 | // factor applied to the equation parameters that are to be optimised. 23 | pub fn gsl_match_crack(main_options: &options::EasiOptions, factors: &mut [f64]) -> f64 { 24 | let all_options = optimise::get_all_options(main_options, &main_options.optimise.file); 25 | 26 | // number of variables to be optimised 27 | let n = factors.len(); // number of optimise parameters 28 | 29 | // Calculate the total number of points to be matched across all 30 | // match files. This may be excessive and could be reduced to one 31 | // value for each measured crack curve. 32 | let m: usize = all_options.iter().fold(0, |sum, option| { 33 | let nrun = 1.0f64; 34 | sum + (option.fracto.len() as f64 - nrun) as usize 35 | }); 36 | 37 | if main_options.verbosity == options::Verbosity::Verbose { 38 | println!("match_crack: n (variables) {} m (targets) {}", n, m); 39 | } 40 | 41 | if m < n { 42 | error!( 43 | "Error: Insufficient number of match points {} to optimise {} variables", 44 | m, n 45 | ); 46 | process::exit(1); 47 | } 48 | 49 | let data = Data { 50 | options: all_options, 51 | params: main_options.params.clone(), 52 | }; 53 | println!("GSL Params {:?}", data.params); 54 | // the optimised variables are multiplied by the parameters 55 | let x = rgsl::types::vector::VectorF64::from_slice(factors).unwrap(); 56 | 57 | rgsl::RngType::env_setup(); 58 | let t = rgsl::MultiFitFdfSolverType::lmsder(); 59 | 60 | let mut f = rgsl::MultiFitFunctionFdf::new(m, n); 61 | 62 | // this is a copy of the code in gsl examples 63 | // let predict_f = clone!(data => move |x, f| { 64 | // println!("In the new lambda: {:?}", x); 65 | // gsl_prediction_error(&x, &mut f, &*data.borrow()) 66 | // }); 67 | 68 | let predict_f = move |x, mut f| { 69 | println!("In the lambda: {:?}", x); 70 | gsl_prediction_error(&x, &mut f, &data) 71 | }; 72 | 73 | f.f = Some(Box::new(predict_f)); 74 | 75 | // let mut f = rgsl::MultiFitFunctionFdf { 76 | // f: gsl_prediction_error, 77 | // df: None, 78 | // fdf: None, 79 | // n: m, 80 | // p: n, 81 | // params: &mut data 82 | // }; 83 | 84 | let mut iter = 0; 85 | let mut s = rgsl::MultiFitFdfSolver::new(&t, m, n).unwrap(); 86 | 87 | s.set(&mut f, &x); 88 | 89 | // print_state(iter as u64, &mut s); 90 | let mut status = rgsl::Value::Continue; 91 | 92 | loop { 93 | if iter >= main_options.optimise.maxiter { 94 | // don't warn for single iteration passes 95 | if (main_options.verbosity == options::Verbosity::Verbose) 96 | && main_options.optimise.maxiter > 0 97 | { 98 | warn!("Warning: we have exceeded the specified maximum iteration limit. Iter = {} > {} {:?}", 99 | iter, main_options.optimise.maxiter, status); 100 | } 101 | break; 102 | } 103 | 104 | iter += 1; 105 | 106 | debug!("before s.x: {:?}", s.x()); 107 | status = s.iterate(); 108 | debug!("after s.x: {:?}", s.x()); 109 | 110 | if status != rgsl::Value::Success { 111 | break; 112 | } 113 | 114 | if main_options.verbosity == options::Verbosity::Verbose { 115 | println!("Checking convergence change in s.dx {:?}", s.dx()); 116 | println!("Checking convergence change in s.x {:?}", s.x()); 117 | } 118 | 119 | status = rgsl::multifit::test_delta(&s.dx(), &s.x(), 1e-10, 1e-10); 120 | if status != rgsl::Value::Continue { 121 | break; 122 | } 123 | } 124 | 125 | if main_options.verbosity == options::Verbosity::Verbose { 126 | println!("status = {}", rgsl::error::str_error(status)); 127 | println!("Finished the optimisation"); 128 | } 129 | 130 | for i in 0..x.len() { 131 | factors[i as usize] = s.x().get(i); 132 | } 133 | 134 | rgsl::blas::level1::dnrm2(&s.f()) 135 | } 136 | 137 | // Wrapper around prediction error to make it compatible with gsl 138 | fn gsl_prediction_error(x: &rgsl::VectorF64, f: &mut rgsl::VectorF64, data: &Data) -> rgsl::Value { 139 | let mut params = Vec::new(); 140 | 141 | // x is the vector of nondimensionalised parameters 142 | for (i, p) in data.params.iter().enumerate() { 143 | params.push(p * x.get(i).max(0.0).min(2.0)); 144 | } 145 | println!("gsl_prediction_error: {:?}", params); 146 | // calculate the error term for each measurement difference 147 | let errors = optimise::prediction_error(¶ms, &data.options); 148 | 149 | // add all the individal error measurements into a global error vector 150 | let mut j = 0; 151 | for growth_error in errors { 152 | for g in growth_error { 153 | f.set(j, g); 154 | j += 1; 155 | } 156 | } 157 | 158 | rgsl::Value::Success 159 | } 160 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | //! Routines for reading and writing files. 2 | 3 | use std::io::{Read, Write}; 4 | use std::fs::File; 5 | use tag::Tag; 6 | use cycle::Cycle; 7 | use std::path::Path; 8 | use std; 9 | use log::error; 10 | 11 | use std::error::Error; 12 | 13 | /// structure to hold measurements 14 | #[derive(Debug, Clone)] 15 | pub struct Measurement { 16 | pub line: usize, 17 | pub block: f64, 18 | pub a: f64, 19 | } 20 | 21 | /// Read in crack growth measurements from a file. 22 | /// 23 | /// The crack data is given in a file containing: 24 | /// ' a' 25 | /// 26 | /// a blank line indicates a break in the continuity of the 27 | /// measurements. is the corresponding line in the load file 28 | /// that the measurement is made (starting at 0). If the line number 29 | /// is not given it is assumed to be 0. Unless there are measurements 30 | /// made at smaller than 1 block intervals the assumption of 0 will 31 | /// not affect the predictions. If is not given then it is 32 | /// assumed the measurements are 1 block apart. 33 | /// 34 | /// For example: 35 | /// 0 2 0.010 36 | /// 15 2 0.013 37 | /// 0 3 0.015 38 | /// 39 | /// 0 5 0.020 40 | /// 15 5 0.024 41 | /// 0 6 0.028 42 | pub fn read_fracto_file(crack_file: &str, nseq: usize) -> Vec { 43 | let meas = read_table(crack_file); 44 | let mut fracto = Vec::new(); 45 | 46 | for (b, m) in meas.iter().enumerate() { 47 | let f = match m.len() { 48 | 0 => Measurement { 49 | line: 0, 50 | block: 0.0, 51 | a: 0.0, 52 | }, 53 | 1 => Measurement { 54 | line: 0, 55 | block: b as f64, 56 | a: m[0], 57 | }, 58 | 2 => Measurement { 59 | line: 0, 60 | block: m[0], 61 | a: m[1], 62 | }, 63 | 3 => Measurement { 64 | line: m[0] as usize, 65 | block: m[1] + m[0] / nseq as f64, 66 | a: m[2], 67 | }, 68 | _ => { 69 | error!("Error: unknown format for fracto file '{}'.", crack_file); 70 | std::process::exit(1) 71 | } 72 | }; 73 | fracto.push(f); 74 | } 75 | 76 | fracto 77 | } 78 | 79 | /// Read a table of data from a file. 80 | pub fn read_table(filename: &str) -> Vec> { 81 | let path = Path::new(filename); 82 | let display = path.display(); 83 | 84 | let mut file = match File::open(&path) { 85 | // The `description` method of `io::Error` returns a string that 86 | // describes the error 87 | Err(why) => { 88 | error!( 89 | "Error: could not open the file '{}': {}.", 90 | display, 91 | Error::description(&why) 92 | ); 93 | std::process::exit(1) 94 | } 95 | Ok(file) => file, 96 | }; 97 | 98 | let mut s = String::new(); 99 | match file.read_to_string(&mut s) { 100 | Err(why) => { 101 | error!( 102 | "Error: could not read {}: {}.", 103 | display, 104 | Error::description(&why) 105 | ); 106 | std::process::exit(1) 107 | } 108 | Ok(_) => true, 109 | }; 110 | 111 | let mut arr = Vec::with_capacity(s.len() / 5); // make a guess at the capacity by assuming 5 characters per line 112 | for l in s.lines() { 113 | // skip any lines starting with comment character 114 | match l.chars().next() { 115 | Some(char) => if char == '#' { 116 | continue; 117 | }, 118 | None => (), 119 | } 120 | 121 | let nums: Vec = l.split_whitespace() 122 | .map(|number| number.parse().unwrap()) 123 | .collect(); 124 | arr.push(nums); 125 | } 126 | 127 | arr 128 | } 129 | 130 | /// Read in a sequence. 131 | /// 132 | /// There can be any number of values on a line in the file 133 | /// and they will all be combined to return the sequence. 134 | pub fn read_sequence(filename: &str) -> Vec { 135 | let file_seq = read_table(filename); 136 | let mut seq = Vec::with_capacity(file_seq.len()); 137 | 138 | for v in file_seq { 139 | for s in v { 140 | seq.push(s); 141 | } 142 | } 143 | Tag::from(&seq) 144 | } 145 | 146 | /// Write out a simple sequence to a file. 147 | pub fn write_sequence(filename: &str, seq: &[Tag]) { 148 | let path = Path::new(filename); 149 | 150 | let mut file = match File::create(&path) { 151 | Err(why) => { 152 | error!("Error: Could not create file '{}': {}.", filename, &why); 153 | std::process::exit(1) 154 | } 155 | Ok(file) => file, 156 | }; 157 | 158 | for load in seq.iter() { 159 | let result = writeln!(&mut file, "{}", load.value); 160 | match result { 161 | Ok(_result) => (), 162 | Err(err) => error!( 163 | "Error: unable to sucessfully write the sequence file - '{}'", 164 | err 165 | ), 166 | } 167 | } 168 | } 169 | 170 | /// Read in a cycle file in AFGROW format: 'max min n' 171 | /// 172 | /// Where the number of cycles is greater than 1 we simply repeat the 173 | /// cycles n in the cycle list to allow us to grow the sequence a 174 | /// cycle at a time. 175 | pub fn read_afgrow_cycles(cyclesfile: &str) -> Vec> { 176 | let mut rcycles = read_table(cyclesfile); 177 | let mut cycles = Vec::new(); 178 | let mut count = 0; 179 | rcycles.remove(0); // skip the first row since it is the 'nsubspectrum nlevels' required by AFGROW 180 | 181 | for c in rcycles { 182 | for _ in 0..c[2] as usize { 183 | cycles.push(Cycle { 184 | max: Tag::new(c[0], count), 185 | min: Tag::new(c[1], count + 1), 186 | }); 187 | count += 2; 188 | } 189 | } 190 | 191 | cycles 192 | } 193 | 194 | /// Write out the cycles in AFGROW format: 'max min 1' 195 | /// i.e. each cycle is written out on a separate line. 196 | pub fn write_cycles(file: &str, cycles: &[Cycle]) { 197 | let mut file = match File::create(&file) { 198 | Err(why) => { 199 | error!("Error: Could not create file '{:?}': {}.", file, why); 200 | std::process::exit(1) 201 | } 202 | Ok(file) => file, 203 | }; 204 | 205 | writeln!(file, "0 {}\n", cycles.len()).unwrap(); 206 | 207 | for &Cycle { min, max } in cycles.iter() { 208 | let result = writeln!(&mut file, "{:?} {:?} 1\n", max.value, min.value); 209 | match result { 210 | Ok(_result) => (), 211 | Err(err) => error!( 212 | "Error: unable to sucessfully write the cycle file '{}'", 213 | err 214 | ), 215 | } 216 | } 217 | } 218 | 219 | #[cfg(test)] 220 | mod tests { 221 | extern crate tempdir; 222 | 223 | use self::tempdir::TempDir; 224 | use cycle; 225 | use tag::Tag; 226 | use io::*; 227 | 228 | #[test] 229 | fn write_cycle_file() { 230 | let tmp_dir = TempDir::new("testing").unwrap(); 231 | let path = tmp_dir.path().join("test-cycle.out"); 232 | 233 | let seq = vec![0.0, 3.0, 2.0, 5.0, 0.0]; 234 | let (rain, _) = cycle::rainflow(&Tag::from(&seq)); 235 | 236 | write_cycles(&path.to_str().unwrap(), &rain); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /doc/help.md: -------------------------------------------------------------------------------- 1 | This is the output from `easigrow --help`. 2 | 3 | ``` 4 | easiGrow: A crack growth modelling program 0.0.0 5 | Paul White 6 | 7 | # easigrow 8 | 9 | Calculates fatigue crack growth rates and finds the 10 | optimum parameters of a crack growth model to match predictions 11 | with measurements. 12 | 13 | The distances to the free edges on the coupon are specified 14 | relative to the origin of the crack. i.e. they are not the coupon 15 | dimensions. The crack depth 'a' is in the `forward` 16 | direction. The crack length 'c' is in the `sideways` 17 | direction. If the crack is not symmetrically located within the 18 | coupon, use the distance to the nearest free edge. 19 | 20 | a) |<-2c->| b) || 21 | ________________ ___ ________________ ___ 22 | | \ / ^ | ^ | | ^ | ^ 23 | | \___/ v a | forward |_ / v a | forward 24 | | | | | 25 | |________________| _v_ |________________| _v_ 26 | 27 | |sideways| |<-- sideways -->| 28 | 29 | Figure: Cross section of a coupon showing crtical dimension names 30 | with a) semi-eliptical crack, and b) corner crack. 31 | 32 | Example 33 | ======== 34 | 35 | easigrow -q ft55.seq -s 300 -r -b seft-newman84 --cycle_method rainflow -d walker:default -a 10e-6 -e 5e-3 -o block,a -n 36 | 10 37 | 38 | 39 | This means: 40 | 41 | Read in the sequence with the filename 'ft55.seq' [-q ft55.seq] 42 | scaling by a factor of 300 MPa [-s 300] and reorder the sequence [-r] 43 | to close all cycles. Use the beta model for a 'semi-elliptical surface 44 | crack in a finite plate in tension' by Newman and Raju [-b 45 | seft-newman84] and calculate the crack size by summing up the growth 46 | increment for each rainflow cycle [--cycle_method rainflow] using the 47 | Walker da/dN equation with default material parameters [-m 48 | walker:default]. Starting at an initial crack size 10 um [-a 10e-6] 49 | grow the crack until a final size 5 mm [-e 5e-3], writing out the 50 | variables 'block' and 'a' [-o block,a] every 10 blocks [-n 10]. 51 | 52 | # How the program works 53 | 54 | Think of the program flow as 55 | 56 | 1. Read in data 57 | 2. Filter the sequence (turning point, rainflow, risefall, deadband etc.) and convert to cycles 58 | 3. Filter the list of cycles 59 | 4. If required, optimise any matching parameters 60 | 5. Perform a crack growth calculation 61 | 6. Write out requested output 62 | 63 | 64 | USAGE: 65 | easigrow [FLAGS] [OPTIONS] 66 | 67 | FLAGS: 68 | 69 | -h, --help Prints help information 70 | -l, --list list all available output variables, growth, beta and da/dN models 71 | --seq_cyclemods only keep those turning points used in remaining cycles 72 | -r, --seq_reorder re-order the sequence to close cycles (default: no reordering) 73 | --seq_tp remove non-turning points from the sequence (default: true) 74 | --summary print summary of sequence 75 | -V, --version Prints version information 76 | -v, --verbose print more information 77 | 78 | OPTIONS: 79 | -e, --limit_a final termination crack sizes (default 1e-3 m) 80 | -a, --astart initial crack sizes (default 10e-6 m) 81 | -b, --beta beta geometry model (default seft-newman84) 82 | --beta_outfile write the beta model to a file 83 | -c, --crack_infile crack growth measurements that will be matched during optimisation 84 | --crack_weight weighting factor for matching crack growth curve (default 1.0) 85 | --cycle_deadband eliminates cycles with turning points within this band 86 | --cycle_infile read the cycles from an AFGROW input file 87 | --cycle_max any range greater than this will be set to this value 88 | --cycle_method method used to extract cycles from sequence (default rainflow) [possible values: 89 | rainflow, tension] 90 | --cycle_min any range less than this will be set to this 91 | --cycle_outfile write the modified sequence to a file 92 | --cycle_rem_big remove cycles with a range bigger than this 93 | --cycle_rem_small remove cycles with a range smaller than this 94 | -d, --dadn dadn material equation (default white:barter14_aa7050-t7451) 95 | --forward distance forwards from crack origin to free edge in 'a' direction of growth 96 | (default: infinite) 97 | --image_bar size of scale bar for image (default 50e-6 m) 98 | --image_outfile generate a pseudo image of the fracture surface and write to FILE 99 | --image_size size of image VxH pixels (default 300x8000) 100 | --image_type type of image output (default sem) [possible values: Sem, Optical] 101 | -N, --limit_block maximum number of blocks (default +1000) 102 | --limit_k1c fracture toughness K1C for termination (default 33 MPa sqrt(m)) 103 | --limit_yield yield or flow stress for plastic zone calculations and net section yield (default 104 | 450 MPa). 105 | --opt_infile optimises model parameters to match a set of target crack growth data. 106 | --opt_max maximum number of iterations to be used for optimisation (default 100) 107 | --opt_method method used for optimisation (default Levenberg) [possible values: Sweep, Nelder, 108 | All] 109 | --opt_nelder nelder-mead parameters (default step: 0.1, alpha: 1.0, gamma: 2.0, rho: 0.5, 110 | sigma: 0.5) 111 | --opt_sweep perform a brute force sweep over a range of scaling factors applied to M dadn 112 | parameters 113 | --opt_tol terminate optimisation when the change in error function is less than this amount 114 | (default 1e-3) 115 | -n, --output_every output crack growth data every +N blocks or -N cycles (default +1) 116 | --output_lines output growth at the given load lines l1,l2,l3... (default 1) 117 | -o, --output_vars comma separated list of variables to be output (default block,a) 118 | -p, --parameters material parameters (default parameters for white:barter14_aa7050-t7451) 119 | --radius radius of hole or notch (default: infinite) 120 | -s, --scale scale factor for load sequence (no default) 121 | -q, --seq_infile file of loading sequence for one block of loading 122 | --seq_max any value greater than this will be set to this value 123 | --seq_min any value less than this will be set to this 124 | --seq_outfile write the modified sequence to a file 125 | --seq_rem_big any values greater than this will be removed 126 | --seq_rem_small any value less than this will be removed 127 | --sequence explicitly set the sequence of loading (default: [0.0, 1.0, 0.0]) 128 | --sideways distance sideways from crack origin to free edge in 'c' direction of growth 129 | (default: infinite) 130 | --youngs Young's modulus for compact tension coupon displacements (default 71000 MPa) 131 | ``` 132 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | /// This is a gernalisation of the dadn interpolation into a general 2 | /// table interpolation 3 | 4 | use io; 5 | use std::{fmt, process, f64}; 6 | 7 | use log::error; 8 | 9 | #[cfg(feature = "GSL")] 10 | pub use table_gsl::PairTable; 11 | 12 | #[cfg(not(feature = "GSL"))] 13 | pub use table_bspline::PairTable; 14 | 15 | /// Interpolation data structure 16 | pub struct Table { 17 | /// columns 18 | pub columns: Vec, 19 | /// rows 20 | pub row: Vec, 21 | /// vectors of columns 22 | pub values: Vec>, 23 | /// Stores the table data in a PairTable 24 | pub table: PairTable, 25 | } 26 | 27 | // transpose a simple vector of vectors (Ugly I know) 28 | pub fn transpose(table: &[Vec]) -> Vec> { 29 | let mut transposed = Vec::new(); 30 | for _r in &table[0] { 31 | transposed.push(Vec::new()); 32 | } 33 | 34 | for row in table { 35 | for (i, cell) in row.iter().enumerate() { 36 | transposed[i].push(*cell); 37 | } 38 | } 39 | transposed 40 | } 41 | 42 | /// Read two parameter table data from a file. 43 | impl Table { 44 | // Read in a file in standard da/dn format 45 | // Comment lines start with # 46 | // The first valid line contains the values of the column variable 47 | // The next lines consist of row variables, followed by the values for each 48 | // of the colummn variables: value_c1 value_c2 value_c3... . 49 | pub fn read_file(interpfile: &str, invert: bool) -> Table { 50 | let mut table = io::read_table(interpfile); // raw data 51 | let columns = table.remove(0); // First row is a list of the r values 52 | 53 | let row: Vec = table.iter_mut().map(|x| x.remove(0)).collect(); 54 | 55 | let mut rows = vec![]; 56 | for _i in 0..columns.len() { 57 | rows.push(row.clone()); 58 | rows.push(row.clone()); 59 | } 60 | 61 | // transpose the table so the elements are the columns 62 | let table = transpose(&table); 63 | let pair_table = if invert { 64 | PairTable::new(columns.clone(), table.clone(), rows.clone()) 65 | } else { 66 | PairTable::new(columns.clone(), rows.clone(), table.clone()) 67 | }; 68 | 69 | Table { 70 | columns, 71 | row, 72 | values: table, 73 | table: pair_table, 74 | } 75 | } 76 | 77 | pub fn interp(&self, row: f64, column: f64) -> f64 { 78 | self.table.interp(row, column) 79 | } 80 | 81 | pub fn new(columns: Vec, row: Vec, values: Vec>, invert: bool) -> Table { 82 | let mut rows = vec![]; 83 | for _i in 0..columns.len() { 84 | rows.push(row.clone()); 85 | } 86 | 87 | let pair_table = if invert { 88 | PairTable::new(columns.clone(), values.clone(), rows.clone()) 89 | } else { 90 | PairTable::new(columns.clone(), rows.clone(), values.clone()) 91 | }; 92 | 93 | Table { 94 | columns, 95 | row, 96 | values, 97 | table: pair_table, 98 | } 99 | } 100 | 101 | /// verifies that the data in the table is consistent 102 | pub fn consistent(&self) -> bool { 103 | // check that there the correct number of columns 104 | for acol in &self.values { 105 | if self.row.len() != acol.len() { 106 | error!("Error: row {:?} != value {:?}", self.row, acol); 107 | return false; 108 | } 109 | } 110 | 111 | true 112 | } 113 | } 114 | 115 | impl fmt::Display for Table { 116 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 117 | let _ = writeln!( 118 | f, 119 | "Spline interpolated tabular lookup 120 | row columns: {:?}", 121 | self.columns 122 | ); 123 | for i in 0..self.row.len() { 124 | let _ = write!(f, "{:10.3e}: ", self.row[i]); 125 | for j in 0..self.values.len() { 126 | let _ = write!(f, "{:10} ", self.values[j][i]); 127 | } 128 | let _ = writeln!(f); 129 | } 130 | write!(f, "") 131 | } 132 | } 133 | 134 | /// PairTable is a different format from Table. 135 | /// 136 | /// In table the rows are common to all columns. In PairTable there is 137 | /// a specific row for each column. In addition the rows may be of 138 | /// unequal sizes, which means they are stored in row format. 139 | impl PairTable { 140 | // Read in a file in standard da/dn format 141 | // Comment lines start with # 142 | // column variables 143 | // row variables 144 | // column1 145 | pub fn read_pair_file(file: &str) -> PairTable { 146 | let mut file_table = io::read_table(file); // raw data 147 | let columns = file_table.remove(0); // First row is a list of the r values 148 | 149 | let mut rows = vec![]; 150 | let mut values = vec![]; 151 | let mut i = 0; 152 | while i < columns.len() { 153 | rows.push(file_table[i].clone()); 154 | values.push(file_table[i + 1].clone()); 155 | i += 2; 156 | } 157 | 158 | let table = PairTable::new(columns, rows, values); 159 | 160 | if !table.consistent() { 161 | println!( 162 | "Error: in PairTable file {}, the file is inconsistent", 163 | file 164 | ); 165 | process::exit(1); 166 | } 167 | 168 | table 169 | } 170 | 171 | /// verifies that the data in the table is consistent 172 | pub fn consistent(&self) -> bool { 173 | // check that there the correct number of columns 174 | for i in 0..self.columns.len() { 175 | if self.rows[i].len() != self.values[i].len() { 176 | println!( 177 | "Error: row {:?} != value {:?}", 178 | self.rows[i], self.values[i] 179 | ); 180 | return false; 181 | } 182 | } 183 | 184 | true 185 | } 186 | } 187 | 188 | impl fmt::Display for PairTable { 189 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 190 | let _ = writeln!( 191 | f, 192 | "Spline interpolated tabular lookup 193 | row columns: {:?}", 194 | self.columns 195 | ); 196 | for i in 0..self.rows.len() { 197 | for j in 0..self.values.len() { 198 | let _ = write!(f, "{:10.3e}: ", self.rows[j][i]); 199 | let _ = write!(f, "{:10} ", self.values[j][i]); 200 | } 201 | let _ = writeln!(f); 202 | } 203 | write!(f, "") 204 | } 205 | } 206 | 207 | // Find the position of the best surrounding values. 208 | pub fn nearest(r: T, rs: &[T]) -> usize { 209 | match rs.iter().rposition(|&x| x < r) { 210 | Some(x) => x, 211 | _ => 0, 212 | } 213 | } 214 | 215 | #[cfg(test)] 216 | mod tests { 217 | use super::*; 218 | 219 | #[test] 220 | fn check_reading_dadn_file() { 221 | let x = Table::read_file("data/dadn/barter14.dadn", true); 222 | let tol = 1e-6; 223 | 224 | assert!((x.interp(0.45, 0.0) - 1.0e-12).abs() < tol); 225 | // outside of interpolation region 226 | // assert!((x.interp(21.45, 0.0) - 1e-5).abs() < tol); 227 | 228 | assert!((x.interp(0.42, 0.3) - 1e-12).abs() < tol); 229 | // outside of interpolation region 230 | // assert!((x.interp(15.53, 0.3) - 1e-5).abs() < tol); 231 | 232 | assert!((x.interp(1.91, 0.4) - 1e-9).abs() < tol); 233 | assert!((x.interp(2.5, 0.4) - 2.696_498_799_927_166_3e-9).abs() < tol); 234 | 235 | assert!((x.interp(0.36, 0.7) - 1e-12).abs() < tol); 236 | // outside of interpolation region 237 | // assert!((x.interp(7.22, 0.7) - 1e-5).abs() < tol); 238 | 239 | assert!((x.interp(0.33, 0.8) - 1e-12).abs() < tol); 240 | // outside of interpolation region 241 | //assert!((x.interp(5.00, 0.8) - 1e-5).abs() < tol); 242 | } 243 | 244 | #[test] 245 | fn near() { 246 | let rs = vec![1usize, 2, 3, 4, 5, 6, 7, 8, 9]; 247 | 248 | assert_eq!(nearest(0, &rs), 0); 249 | assert_eq!(nearest(3, &rs), 1); 250 | assert_eq!(nearest(10, &rs), 8); 251 | assert_eq!(nearest(20, &rs), 8); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | /// These are the data stuctures for command line options as well as 2 | /// the initial default values. 3 | 4 | use std; 5 | use std::f64; 6 | use std::string::String; 7 | use nelder::Nelder; 8 | use fatigue::{cycle, fracto, grow, io, material, tag}; 9 | use fatigue::COMMENT; 10 | 11 | #[cfg(not(feature = "GSL"))] 12 | arg_enum!{ 13 | #[derive(Debug, Clone)] 14 | pub enum OptimMethod { 15 | Sweep, 16 | Nelder, 17 | All 18 | } 19 | } 20 | 21 | #[cfg(feature = "GSL")] 22 | arg_enum!{ 23 | #[derive(Debug, Clone)] 24 | pub enum OptimMethod { 25 | Sweep, 26 | Nelder, 27 | Levenberg, 28 | All 29 | } 30 | } 31 | 32 | arg_enum!{ 33 | #[derive(Debug, Clone)] 34 | pub enum CycleMethod { 35 | Rainflow, 36 | Tension 37 | } 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct Optimise { 42 | pub file: String, 43 | pub method: OptimMethod, 44 | pub maxiter: usize, 45 | pub tol: f64, 46 | pub nelder: Nelder, 47 | pub sweep: Vec, 48 | } 49 | 50 | #[derive(Debug, Clone, PartialEq)] 51 | pub enum Verbosity { 52 | Verbose, 53 | Terse, 54 | } 55 | #[derive(Debug, Clone, PartialEq)] 56 | pub enum TerminatingOutput { 57 | List, 58 | Summary, 59 | None, 60 | } 61 | 62 | /// Option data for performing a crack growth calculation. 63 | #[derive(Debug, Clone)] 64 | pub struct EasiOptions { 65 | /// Initial starting size, last value in vector is considered as c the crack width. 66 | pub a: Vec, 67 | /// Final crack depth. 68 | pub a_limit: Vec, 69 | /// Maximum number of cycles to run for. 70 | pub block_limit: f64, 71 | /// Type of output 72 | pub output: TerminatingOutput, 73 | /// Level of verbosity 74 | pub verbosity: Verbosity, 75 | /// Name of inbuilt dadn data to use. 76 | pub dadn: String, 77 | /// Dadn parameters to be used in optimisation or any crack calculation. 78 | pub params: Vec, 79 | /// Closure parameters 80 | pub closure: Vec, 81 | /// Parameters to be written out. 82 | pub output_vars: Vec, 83 | /// Output 'every' block. 84 | pub output_every: i32, 85 | /// Sequence lines to write out 86 | pub output_lines: Vec, 87 | /// Name of damage model to use. 88 | pub cycle_method: cycle::CycleMethod, 89 | /// Scale the sequence by this factor, typically stress 90 | pub scale: f64, 91 | /// Beta model to use. 92 | pub beta: String, 93 | pub beta_outfile: String, 94 | /// Information describing the shape of the component. 95 | pub component: grow::Component, 96 | /// Sequence to be used. 97 | pub sequence: Vec, 98 | /// Simplified version of the measured crack file. 99 | pub fracto: Vec, 100 | /// Data from crackfile. 101 | pub cycles: Vec>, 102 | /// Info for optimisation. 103 | pub optimise: Optimise, 104 | /// Data for generating a reconstructed image. 105 | pub image: fracto::ImageData, 106 | /// Name of crack growth data file used for target in optimisation. 107 | pub crack_infile: String, 108 | // Weighting factor for crack errors 109 | pub crack_weight: f64, 110 | /// Sequence information. 111 | pub seq_mods: cycle::SequenceModifiers, 112 | pub cycle_mods: cycle::CycleModifiers, 113 | /// Filename of sequence. 114 | pub seq_infile: String, 115 | /// Name of cycle file for inputting data. 116 | pub cycle_infile: String, 117 | } 118 | 119 | 120 | impl std::fmt::Display for EasiOptions { 121 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 122 | let _e = writeln!(f, "{}a: {:?}", COMMENT, self.a); 123 | let _e = writeln!(f, "{}a_limit: {:?}", COMMENT, self.a_limit); 124 | let _e = writeln!(f, "{}block_limit: {:?}", COMMENT, self.block_limit); 125 | let _e = writeln!(f, "{}params: {:?}", COMMENT, self.params); 126 | let _e = writeln!(f, "{}output_vars: {:?}", COMMENT, self.output_vars); 127 | let _e = writeln!(f, "{}output_every: {:?}", COMMENT, self.output_every); 128 | let _e = writeln!(f, "{}output_lines: {:?}", COMMENT, self.output_lines); 129 | let _e = writeln!(f, "{}scale: {:?}", COMMENT, self.scale); 130 | let _e = writeln!(f, "{}beta: {:?}", COMMENT, self.beta); 131 | let _e = writeln!(f, "{}component: {:?}", COMMENT, self.component); 132 | let _e = writeln!(f, "{}seq_infile: {:?}", COMMENT, self.seq_infile); 133 | let _e = writeln!(f, "{}seq_mods: {:?}", COMMENT, self.seq_mods); 134 | let _e = writeln!(f, "{}cycle_method: {:?}", COMMENT, self.cycle_method); 135 | let _e = writeln!(f, "{}cycle_infile: {:?}", COMMENT, self.cycle_infile); 136 | let _e = writeln!(f, "{}cycle_mods: {:?}", COMMENT, self.cycle_mods); 137 | 138 | if self.optimise.file != "" { 139 | let _e = writeln!(f, "{}optimise: {:?}", COMMENT, self.optimise); 140 | let _e = writeln!(f, "{}crack_infile: {:?}", COMMENT, self.crack_infile); 141 | let _e = writeln!(f, "{}crack_weight: {:?}", COMMENT, self.crack_weight); 142 | } 143 | 144 | if self.image.file != "" { 145 | let _e = writeln!(f, "{}fracto: {:?}", COMMENT, self.fracto); 146 | let _e = writeln!(f, "{}image: {:?}", COMMENT, self.image); 147 | } 148 | write!(f, "{}dadn: {:?}", COMMENT, self.dadn) 149 | } 150 | } 151 | 152 | /// read in each sequence and the measured crack growth file 153 | /// 154 | /// This stuff only needs to be performed once such as populating the 155 | /// values of tables read from files. 156 | pub fn read_all_files(options: &mut EasiOptions) { 157 | if !options.seq_infile.is_empty() { 158 | options.sequence = io::read_sequence(&options.seq_infile); 159 | } 160 | 161 | if !options.cycle_infile.is_empty() { 162 | options.cycles = io::read_afgrow_cycles(&options.cycle_infile); 163 | } 164 | 165 | if !options.crack_infile.is_empty() { 166 | options.fracto = io::read_fracto_file(&options.crack_infile, options.sequence.len()); 167 | } 168 | } 169 | 170 | pub fn get_default_options() -> EasiOptions { 171 | // default options for easigro 172 | EasiOptions { 173 | // crack growth formulation 174 | a: vec![10_e-6f64, 10e-6], 175 | dadn: "white:barter14-aa7050t7451".to_string(), 176 | params: vec![], 177 | closure: vec![0.3, 0.5], 178 | beta: "seft-newman84".to_string(), 179 | beta_outfile: "".to_string(), 180 | component: grow::Component { 181 | sideways: f64::INFINITY, 182 | forward: f64::INFINITY, 183 | radius: f64::INFINITY, 184 | material: material::Properties { 185 | yield_stress: 450.0, 186 | k1c: 33.0, 187 | youngs_modulus: 71e3, 188 | }, 189 | }, 190 | 191 | // termination criteria 192 | a_limit: vec![1e-3, 1e-3], // (m) 193 | block_limit: 1000.0, 194 | 195 | // sequence info 196 | scale: 0.0, // MPa 197 | seq_infile: "".to_string(), // name of sequence file 198 | sequence: tag::Tag::from(&[0.0, 1.0, 0.0]), // Constant amplitude cycle 199 | cycles: vec![], // cycles from 200 | cycle_infile: "".to_string(), 201 | cycle_method: cycle::CycleMethod::Rainflow, 202 | seq_mods: cycle::SequenceModifiers { 203 | cap_max: None, 204 | cap_min: None, 205 | remove_bigger: None, 206 | remove_smaller: None, 207 | cycles: false, 208 | reorder: false, 209 | turning_points: false, 210 | outfile: None, 211 | }, 212 | cycle_mods: cycle::CycleModifiers { 213 | cap_max: None, 214 | cap_min: None, 215 | remove_bigger: None, 216 | remove_smaller: None, 217 | remove_region: None, 218 | outfile: None, 219 | }, 220 | 221 | // crack comparison 222 | crack_infile: "".to_string(), // name of fracto file 223 | crack_weight: 1.0, 224 | fracto: vec![], 225 | 226 | // solution type 227 | optimise: Optimise { 228 | file: "".to_string(), 229 | maxiter: 100, 230 | method: OptimMethod::Nelder, 231 | tol: 1e-4, 232 | nelder: Nelder::default(), 233 | sweep: vec![0.8, 1.0, 1.25], 234 | }, 235 | 236 | image: fracto::ImageData { 237 | file: "".to_string(), 238 | barlength: 50e-6, 239 | xsize: 300, 240 | ysize: 8000, 241 | image: fracto::ImageType::Sem, 242 | }, 243 | 244 | // output options 245 | output_vars: vec!["block".to_string(), "a".to_string()], 246 | output_every: 1, // output 'every' block 247 | output_lines: vec![1], 248 | output: TerminatingOutput::None, 249 | verbosity: Verbosity::Terse, 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/fracto.rs: -------------------------------------------------------------------------------- 1 | //! Generate a fracto image given the cycle by cycle crack growth 2 | //! history. 3 | //! 4 | //! This currently creates a pseudo image where the color of each band 5 | //! is a function of the Kmax. But in the future it should be a 6 | //! rendered fracture surface based on the angle of growth. The image 7 | //! is output in svg format where each cycle is represented as a line 8 | //! in the image. Because of this, it may be best to restrict the 9 | //! number of cycles by limiting the starting and ending crack sizes. 10 | //! The start of each block is indicated by drawing a red line through 11 | //! the image. A periodic white scale bar is drawn at the bottom of 12 | //! the image. The scale of the bar and the size of the image can be 13 | //! changed. 14 | 15 | use grow::History; 16 | use std::str::FromStr; 17 | use numbers::NonNan; 18 | 19 | // create the image using SVG 20 | use svg::Document; 21 | use svg::node::element; 22 | use svg; 23 | 24 | /// Data for generation of a reconstructed image. 25 | #[derive(Debug, Clone)] 26 | pub struct ImageData { 27 | /// Name of pseudo image file. 28 | pub file: String, 29 | /// Size of scale bar for image. 30 | pub barlength: f64, 31 | pub xsize: u32, 32 | pub ysize: u32, 33 | pub image: ImageType, 34 | } 35 | 36 | /// Image generation types. 37 | #[derive(Debug, Clone)] 38 | pub enum ImageType { 39 | Sem, 40 | Optical, 41 | } 42 | 43 | /// Implement the trait to get `ImageType` from a string. 44 | impl FromStr for ImageType { 45 | type Err = &'static str; 46 | 47 | fn from_str(s: &str) -> Result { 48 | match s { 49 | "Sem" | "sem" => Ok(ImageType::Sem), 50 | "Optical" | "optical" => Ok(ImageType::Optical), 51 | _ => Err("no match"), 52 | } 53 | } 54 | } 55 | 56 | /// Write an image to a file. 57 | // pub fn write(filename: &str, image: ImageBuffer, Vec>) { 58 | // let ref mut fout = File::create(&Path::new(filename)).unwrap(); 59 | 60 | // // We must indicate the image's color type and what format to save as. 61 | // let _ = ImageLuma8(image).save(fout, PNG); 62 | // } 63 | 64 | // color models return a value 0.0 to 1.0 65 | // simple color model based on the r ratio 66 | //fn r_color_model(cycle: &History) -> f64 { 67 | // cap the r ratio 68 | // let r: f64 = if cycle.r < 0.0 {0.0} else {cycle.r}; 69 | // r.powf(0.4) 70 | //} 71 | 72 | /// Determines the color of a marker band. 73 | /// Essentially the angle of the fracture plane to the average crack plane. Dark regions reflect the light away. 74 | /// This model indicate the color is based on Kmax. 75 | fn kmax_color_model(cycle: &History, cycle_max: &History) -> f64 { 76 | // cap the r ratio 77 | // println!("color K: {} {}", cycle.k[0], cycle_max.k[0]); 78 | // (cycle.k[0] / cycle_max.k[0]).max(0.0).powf(0.6).min(1.0) 79 | 80 | // The max min limits limit the display to a nice grey tone rather 81 | // than having black or white bands 82 | (cycle.k[0] / cycle_max.k[0]).powf(3.0).min(0.65).max(0.45) 83 | 84 | } 85 | 86 | /// Make a pseudo image of the fracture surface. 87 | /// 88 | /// Fit the entire crack growth history into the image frame. 89 | pub fn write_svg_pseudo_image(history: &[History], frame: &ImageData) { 90 | let mut document = Document::new().set("viewBox", (0, 0, frame.ysize, frame.xsize)); 91 | 92 | let lightest = 1.0; 93 | println!("frame: {:?}", frame); 94 | // calculate the maximum cycle in the history 95 | // let history_max = grow::cumulative_history(history); 96 | println!("cycles in history {}", history.len()); 97 | 98 | // check that the last point is the maximum 99 | let a_max = history[history.len() - 1].crack.a[0]; 100 | let a_init = history[0].crack.a[0]; 101 | println!("history {}, a_max {}", history.len(), a_max); 102 | 103 | let mut a_prev = a_init; 104 | println!("starting crack size is {}", a_prev); 105 | let mut delta_a = 0.0; 106 | let mut sum_color = 0.0; 107 | let mut ave_color; 108 | let mut y_prev = 0.0; 109 | let nbar = 10i32; // put this number of micron bars along the image 110 | 111 | //average over the length of a pixel 112 | let pixel_size = (a_max - a_init) / f64::from(frame.ysize); // size of a pixel 113 | println!("Pixel size is {:.3e}", pixel_size); 114 | let mut cblock = history[0].block.floor() + 1.0; 115 | let bar_pixels = (frame.barlength / pixel_size) as u32; 116 | 117 | // cover the canvas in black 118 | let path = element::Rectangle::new() 119 | .set("fill", "black") 120 | .set("stroke", 0) 121 | .set("y", 0) 122 | .set("x", 0) 123 | .set("width", frame.ysize) 124 | .set("height", frame.xsize); 125 | 126 | document = document.add(path); 127 | let cycle_max = history.iter().max_by(|h0, h| NonNan::new(h0.k[0]).cmp(&NonNan::new(h.k[0]))).unwrap(); 128 | println!("cycle max: {:?}", cycle_max); 129 | 130 | // plot each cycle in the history 131 | for cycle in history.iter() { 132 | // println!("{:?}", cycle); 133 | let striation = cycle.crack.a[0] - a_prev; // the width of the current striation 134 | a_prev = cycle.crack.a[0]; 135 | 136 | // keep adding to the increment of growth until we have a single pixel big enough to display/ 137 | if delta_a < pixel_size { 138 | delta_a += striation; 139 | sum_color += kmax_color_model(cycle, cycle_max) * striation; // weight the color according to the striation 140 | continue; 141 | } 142 | 143 | ave_color = sum_color / delta_a; 144 | sum_color = 0.0; 145 | delta_a = 0.0; 146 | 147 | let y_cur = f64::from(frame.ysize) * ((cycle.crack.a[0] - a_init) / (a_max - a_init)); 148 | let newblock = if cycle.block > cblock { 149 | cblock = cycle.block.floor() + 1.0; 150 | true 151 | } else { 152 | false 153 | }; 154 | 155 | // color in all the pixels from the previous pixel to the current pixel position 156 | let color = (lightest - ave_color).max(0.0); 157 | let shade = (color * 16.0) as u8; 158 | let code = format!("#{:x}{:x}{:x}", shade, shade, shade); 159 | // println!("Average colour {}", ave_color); 160 | let path = element::Rectangle::new() 161 | .set("fill", code) 162 | .set("stroke", 0) 163 | .set("y", 0) 164 | .set("x", y_prev) 165 | .set("width", y_cur - y_prev) 166 | .set("height", frame.xsize - 50); 167 | 168 | document = document.add(path); 169 | 170 | // draw a line at the position of the start of the block 171 | if newblock { 172 | let start = element::Rectangle::new() 173 | .set("fill", "red") 174 | .set("y", 0.0) 175 | .set("x", y_prev) 176 | .set("height", frame.xsize) 177 | .set("width", 2.0); 178 | 179 | document = document.add(start); 180 | } 181 | 182 | y_prev = y_cur; 183 | } 184 | 185 | // add a micron bar every nbar spacings 186 | for b in 0..nbar { 187 | let bar_pos = b * frame.ysize as i32 / nbar; 188 | let bar_marker = element::Rectangle::new() 189 | .set("fill", "white") 190 | .set("stroke", 0) 191 | .set("y", frame.xsize - 40) 192 | .set("x", bar_pos) 193 | .set("width", bar_pixels) 194 | .set("height", 10); 195 | 196 | document = document.add(bar_marker); 197 | } 198 | 199 | // set the background to white 200 | // let path = element::Rectangle::new() 201 | // .set("fill", "white") 202 | // .set("stroke", 0) 203 | // .set("y", frame.xsize - 2) 204 | // .set("x", 0) 205 | // .set("width", frame.ysize) 206 | // .set("height", 2); 207 | 208 | // document = document.add(path); 209 | 210 | svg::save(&frame.file, &document).unwrap(); 211 | } 212 | 213 | // put a micron bar on the image 214 | // fn pixel_micron_bar(imgx: u32, ypos: u32, imbuf: &mut ImageBuffer, Vec>, npixels: u32) { 215 | // // scale bar sizes 216 | // let bar_pwidth = 10u32; 217 | // let bar_color = Luma([255 as u8]); 218 | 219 | // // put micron along the length of the image 220 | // for z in 0u32..npixels { 221 | // for x in imgx - 5 - bar_pwidth..imgx - 5 { 222 | // imbuf.put_pixel(ypos + z, x, bar_color); 223 | // } 224 | // } 225 | // } 226 | 227 | #[cfg(test)] 228 | mod tests { 229 | use super::*; 230 | use cycle::Cycle; 231 | use plastic::ZoneWidth; 232 | use grow::CrackState; 233 | use tag::Tag; 234 | use std::path::Path; 235 | use std::fs; 236 | 237 | #[test] 238 | fn test_make_pseudo_image() { 239 | let mut history = Vec::new(); 240 | 241 | let da = 1e-7; 242 | let mut a = 1e-6; 243 | let mut block; 244 | 245 | let hist = |a, block, peak| History { 246 | block: block, 247 | stress: 300.0, 248 | cycle: Cycle::new(Tag::new(peak, 0), Tag::new(0.0, 0)), 249 | k: vec![peak], 250 | dk: vec![peak], 251 | beta: vec![1.0], 252 | da: vec![da], 253 | crack: CrackState { 254 | a: vec![a], 255 | mono_zone_extent: ZoneWidth{ plane_stress: 0.1, plane_strain: 0.1 }, 256 | cyclic_zone_extent: ZoneWidth{ plane_stress: 0.1, plane_strain: 0.1 }, 257 | }, 258 | }; 259 | 260 | for b in 5..10 { 261 | for i in 0..100 { 262 | a += da; 263 | block = b as f64 + i as f64 / 200.0; 264 | history.push(hist(a, block, 1.0)); 265 | } 266 | for i in 0..100 { 267 | a += da; 268 | block = b as f64 + 0.5 + i as f64 / 200.0; 269 | history.push(hist(a, block, 0.5)); 270 | } 271 | } 272 | 273 | let image = ImageData { file: "/tmp/fracto-test.svg".to_string(), 274 | barlength: 5e-6, 275 | xsize:300, 276 | ysize: 800, 277 | image: ImageType::Sem, 278 | }; 279 | 280 | write_svg_pseudo_image(&history, &image) ; 281 | assert!(Path::new(&image.file).exists()); 282 | let _r = fs::remove_file(image.file); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /data/sequences/rainflow-seq1.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 0.75 3 | 0.25 4 | 1 5 | 0 6 | 0.75 7 | 0.25 8 | 1 9 | 0 10 | 0.75 11 | 0.25 12 | 1 13 | 0 14 | 0.75 15 | 0.25 16 | 1 17 | 0 18 | 0.75 19 | 0.25 20 | 1 21 | 0 22 | 0.75 23 | 0.25 24 | 1 25 | 0 26 | 0.75 27 | 0.25 28 | 1 29 | 0 30 | 0.75 31 | 0.25 32 | 1 33 | 0 34 | 0.75 35 | 0.25 36 | 1 37 | 0 38 | 0.75 39 | 0.25 40 | 1 41 | 0 42 | 0.75 43 | 0.25 44 | 1 45 | 0 46 | 0.75 47 | 0.25 48 | 1 49 | 0 50 | 0.75 51 | 0.25 52 | 1 53 | 0 54 | 0.75 55 | 0.25 56 | 1 57 | 0 58 | 0.75 59 | 0.25 60 | 1 61 | 0 62 | 0.75 63 | 0.25 64 | 1 65 | 0 66 | 0.75 67 | 0.25 68 | 1 69 | 0 70 | 0.75 71 | 0.25 72 | 1 73 | 0 74 | 0.75 75 | 0.25 76 | 1 77 | 0 78 | 0.75 79 | 0.25 80 | 1 81 | 0 82 | 0.75 83 | 0.25 84 | 1 85 | 0 86 | 0.75 87 | 0.25 88 | 1 89 | 0 90 | 0.75 91 | 0.25 92 | 1 93 | 0 94 | 0.75 95 | 0.25 96 | 1 97 | 0 98 | 0.75 99 | 0.25 100 | 1 101 | 0 102 | 0.75 103 | 0.25 104 | 1 105 | 0 106 | 0.75 107 | 0.25 108 | 1 109 | 0 110 | 0.75 111 | 0.25 112 | 1 113 | 0 114 | 0.75 115 | 0.25 116 | 1 117 | 0 118 | 0.75 119 | 0.25 120 | 1 121 | 0 122 | 0.75 123 | 0.25 124 | 1 125 | 0 126 | 0.75 127 | 0.25 128 | 1 129 | 0 130 | 0.75 131 | 0.25 132 | 1 133 | 0 134 | 0.75 135 | 0.25 136 | 1 137 | 0 138 | 0.75 139 | 0.25 140 | 1 141 | 0 142 | 0.75 143 | 0.25 144 | 1 145 | 0 146 | 0.75 147 | 0.25 148 | 1 149 | 0 150 | 0.75 151 | 0.25 152 | 1 153 | 0 154 | 0.75 155 | 0.25 156 | 1 157 | 0 158 | 0.75 159 | 0.25 160 | 1 161 | 0.25 162 | 0.75 163 | 0.25 164 | 0.75 165 | 0.25 166 | 0.75 167 | 0.25 168 | 0.75 169 | 0.25 170 | 0.75 171 | 0.25 172 | 0.75 173 | 0.25 174 | 0.75 175 | 0.25 176 | 0.75 177 | 0.25 178 | 0.75 179 | 0.25 180 | 0.75 181 | 0.25 182 | 0.75 183 | 0.25 184 | 0.75 185 | 0.25 186 | 0.75 187 | 0.25 188 | 0.75 189 | 0.25 190 | 0.75 191 | 0.25 192 | 0.75 193 | 0.25 194 | 0.75 195 | 0.25 196 | 0.75 197 | 0.25 198 | 0.75 199 | 0.25 200 | 0.75 201 | 0.25 202 | 0.75 203 | 0.25 204 | 0.75 205 | 0.25 206 | 0.75 207 | 0.25 208 | 0.75 209 | 0.25 210 | 0.75 211 | 0.25 212 | 0.75 213 | 0.25 214 | 0.75 215 | 0.25 216 | 0.75 217 | 0.25 218 | 0.75 219 | 0.25 220 | 0.75 221 | 0.25 222 | 0.75 223 | 0.25 224 | 0.75 225 | 0.25 226 | 0.75 227 | 0.25 228 | 0.75 229 | 0.25 230 | 0.75 231 | 0.25 232 | 0.75 233 | 0.25 234 | 0.75 235 | 0.25 236 | 0.75 237 | 0.25 238 | 0.75 239 | 0.25 240 | 0.75 241 | 0 242 | 1 243 | 0 244 | 1 245 | 0 246 | 1 247 | 0 248 | 1 249 | 0 250 | 1 251 | 0 252 | 1 253 | 0 254 | 1 255 | 0 256 | 1 257 | 0 258 | 1 259 | 0 260 | 1 261 | 0 262 | 1 263 | 0 264 | 1 265 | 0 266 | 1 267 | 0 268 | 1 269 | 0 270 | 1 271 | 0 272 | 1 273 | 0 274 | 1 275 | 0 276 | 1 277 | 0 278 | 1 279 | 0 280 | 1 281 | 0 282 | 1 283 | 0 284 | 1 285 | 0 286 | 1 287 | 0 288 | 1 289 | 0 290 | 1 291 | 0 292 | 1 293 | 0 294 | 1 295 | 0 296 | 1 297 | 0 298 | 1 299 | 0 300 | 1 301 | 0 302 | 1 303 | 0 304 | 1 305 | 0 306 | 1 307 | 0 308 | 1 309 | 0 310 | 1 311 | 0 312 | 1 313 | 0 314 | 1 315 | 0 316 | 1 317 | 0 318 | 1 319 | 0 320 | 1 321 | 0 322 | 0.75 323 | 0 324 | 0.75 325 | 0 326 | 0.75 327 | 0 328 | 0.75 329 | 0 330 | 0.75 331 | 0 332 | 0.75 333 | 0 334 | 0.75 335 | 0 336 | 0.75 337 | 0 338 | 0.75 339 | 0 340 | 0.75 341 | 0 342 | 0.75 343 | 0 344 | 0.75 345 | 0 346 | 0.75 347 | 0 348 | 0.75 349 | 0 350 | 0.75 351 | 0 352 | 0.75 353 | 0 354 | 0.75 355 | 0 356 | 0.75 357 | 0 358 | 0.75 359 | 0 360 | 0.75 361 | 0 362 | 0.75 363 | 0 364 | 0.75 365 | 0 366 | 0.75 367 | 0 368 | 0.75 369 | 0 370 | 0.75 371 | 0 372 | 0.75 373 | 0 374 | 0.75 375 | 0 376 | 0.75 377 | 0 378 | 0.75 379 | 0 380 | 0.75 381 | 0 382 | 0.75 383 | 0 384 | 0.75 385 | 0 386 | 0.75 387 | 0 388 | 0.75 389 | 0 390 | 0.75 391 | 0 392 | 0.75 393 | 0 394 | 0.75 395 | 0 396 | 0.75 397 | 0 398 | 0.75 399 | 0 400 | 0.75 401 | 0.25 402 | 1 403 | 0.25 404 | 1 405 | 0.25 406 | 1 407 | 0.25 408 | 1 409 | 0.25 410 | 1 411 | 0.25 412 | 1 413 | 0.25 414 | 1 415 | 0.25 416 | 1 417 | 0.25 418 | 1 419 | 0.25 420 | 1 421 | 0.25 422 | 1 423 | 0.25 424 | 1 425 | 0.25 426 | 1 427 | 0.25 428 | 1 429 | 0.25 430 | 1 431 | 0.25 432 | 1 433 | 0.25 434 | 1 435 | 0.25 436 | 1 437 | 0.25 438 | 1 439 | 0.25 440 | 1 441 | 0.25 442 | 1 443 | 0.25 444 | 1 445 | 0.25 446 | 1 447 | 0.25 448 | 1 449 | 0.25 450 | 1 451 | 0.25 452 | 1 453 | 0.25 454 | 1 455 | 0.25 456 | 1 457 | 0.25 458 | 1 459 | 0.25 460 | 1 461 | 0.25 462 | 1 463 | 0.25 464 | 1 465 | 0.25 466 | 1 467 | 0.25 468 | 1 469 | 0.25 470 | 1 471 | 0.25 472 | 1 473 | 0.25 474 | 1 475 | 0.25 476 | 1 477 | 0.25 478 | 1 479 | 0.25 480 | 1 481 | 0 482 | 1 483 | 0.25 484 | 0.75 485 | 0 486 | 1 487 | 0.25 488 | 0.75 489 | 0 490 | 1 491 | 0.25 492 | 0.75 493 | 0 494 | 1 495 | 0.25 496 | 0.75 497 | 0 498 | 1 499 | 0.25 500 | 0.75 501 | 0 502 | 1 503 | 0.25 504 | 0.75 505 | 0 506 | 1 507 | 0.25 508 | 0.75 509 | 0 510 | 1 511 | 0.25 512 | 0.75 513 | 0 514 | 1 515 | 0.25 516 | 0.75 517 | 0 518 | 1 519 | 0.25 520 | 0.75 521 | 0 522 | 1 523 | 0.25 524 | 0.75 525 | 0 526 | 1 527 | 0.25 528 | 0.75 529 | 0 530 | 1 531 | 0.25 532 | 0.75 533 | 0 534 | 1 535 | 0.25 536 | 0.75 537 | 0 538 | 1 539 | 0.25 540 | 0.75 541 | 0 542 | 1 543 | 0.25 544 | 0.75 545 | 0 546 | 1 547 | 0.25 548 | 0.75 549 | 0 550 | 1 551 | 0.25 552 | 0.75 553 | 0 554 | 1 555 | 0.25 556 | 0.75 557 | 0 558 | 1 559 | 0.25 560 | 0.75 561 | 0 562 | 1 563 | 0.25 564 | 0.75 565 | 0 566 | 1 567 | 0.25 568 | 0.75 569 | 0 570 | 1 571 | 0.25 572 | 0.75 573 | 0 574 | 1 575 | 0.25 576 | 0.75 577 | 0 578 | 1 579 | 0.25 580 | 0.75 581 | 0 582 | 1 583 | 0.25 584 | 0.75 585 | 0 586 | 1 587 | 0.25 588 | 0.75 589 | 0 590 | 1 591 | 0.25 592 | 0.75 593 | 0 594 | 1 595 | 0.25 596 | 0.75 597 | 0 598 | 1 599 | 0.25 600 | 0.75 601 | 0 602 | 1 603 | 0.25 604 | 0.75 605 | 0 606 | 1 607 | 0.25 608 | 0.75 609 | 0 610 | 1 611 | 0.25 612 | 0.75 613 | 0 614 | 1 615 | 0.25 616 | 0.75 617 | 0 618 | 1 619 | 0.25 620 | 0.75 621 | 0 622 | 1 623 | 0.25 624 | 0.75 625 | 0 626 | 1 627 | 0.25 628 | 0.75 629 | 0 630 | 1 631 | 0.25 632 | 0.75 633 | 0 634 | 1 635 | 0.25 636 | 0.75 637 | 0 638 | 1 639 | 0.25 640 | 0.75 641 | 0.25 642 | 0.75 643 | 0.25 644 | 0.75 645 | 0.25 646 | 0.75 647 | 0.25 648 | 0.75 649 | 0.25 650 | 0.75 651 | 0.25 652 | 0.75 653 | 0.25 654 | 0.75 655 | 0.25 656 | 0.75 657 | 0.25 658 | 0.75 659 | 0.25 660 | 0.75 661 | 0.25 662 | 0.75 663 | 0.25 664 | 0.75 665 | 0.25 666 | 0.75 667 | 0.25 668 | 0.75 669 | 0.25 670 | 0.75 671 | 0.25 672 | 0.75 673 | 0.25 674 | 0.75 675 | 0.25 676 | 0.75 677 | 0.25 678 | 0.75 679 | 0.25 680 | 0.75 681 | 0.25 682 | 0.75 683 | 0.25 684 | 0.75 685 | 0.25 686 | 0.75 687 | 0.25 688 | 0.75 689 | 0.25 690 | 0.75 691 | 0.25 692 | 0.75 693 | 0.25 694 | 0.75 695 | 0.25 696 | 0.75 697 | 0.25 698 | 0.75 699 | 0.25 700 | 0.75 701 | 0.25 702 | 0.75 703 | 0.25 704 | 0.75 705 | 0.25 706 | 0.75 707 | 0.25 708 | 0.75 709 | 0.25 710 | 0.75 711 | 0.25 712 | 0.75 713 | 0.25 714 | 0.75 715 | 0.25 716 | 0.75 717 | 0.25 718 | 0.75 719 | 0.25 720 | 0.75 721 | 0.25 722 | 0.75 723 | 0.25 724 | 0.75 725 | 0.25 726 | 0.75 727 | 0.25 728 | 0.75 729 | 0.25 730 | 0.75 731 | 0.25 732 | 0.75 733 | 0.25 734 | 0.75 735 | 0.25 736 | 0.75 737 | 0.25 738 | 0.75 739 | 0.25 740 | 0.75 741 | 0.25 742 | 0.75 743 | 0.25 744 | 0.75 745 | 0.25 746 | 0.75 747 | 0.25 748 | 0.75 749 | 0.25 750 | 0.75 751 | 0.25 752 | 0.75 753 | 0.25 754 | 0.75 755 | 0.25 756 | 0.75 757 | 0.25 758 | 0.75 759 | 0.25 760 | 0.75 761 | 0.25 762 | 0.75 763 | 0.25 764 | 0.75 765 | 0.25 766 | 0.75 767 | 0.25 768 | 0.75 769 | 0.25 770 | 0.75 771 | 0.25 772 | 0.75 773 | 0.25 774 | 0.75 775 | 0.25 776 | 0.75 777 | 0.25 778 | 0.75 779 | 0.25 780 | 0.75 781 | 0.25 782 | 0.75 783 | 0.25 784 | 0.75 785 | 0.25 786 | 0.75 787 | 0.25 788 | 0.75 789 | 0.25 790 | 0.75 791 | 0.25 792 | 0.75 793 | 0.25 794 | 0.75 795 | 0.25 796 | 0.75 797 | 0.25 798 | 0.75 799 | 0.25 800 | 0.75 801 | 0.25 802 | 0.75 803 | 0.25 804 | 0.75 805 | 0.25 806 | 0.75 807 | 0.25 808 | 0.75 809 | 0.25 810 | 0.75 811 | 0.25 812 | 0.75 813 | 0.25 814 | 0.75 815 | 0.25 816 | 0.75 817 | 0.25 818 | 0.75 819 | 0.25 820 | 0.75 821 | 0.25 822 | 0.75 823 | 0.25 824 | 0.75 825 | 0.25 826 | 0.75 827 | 0.25 828 | 0.75 829 | 0.25 830 | 0.75 831 | 0.25 832 | 0.75 833 | 0.25 834 | 0.75 835 | 0.25 836 | 0.75 837 | 0.25 838 | 0.75 839 | 0.25 840 | 0.75 841 | 0.25 842 | 0.75 843 | 0.25 844 | 0.75 845 | 0.25 846 | 0.75 847 | 0.25 848 | 0.75 849 | 0.25 850 | 0.75 851 | 0.25 852 | 0.75 853 | 0.25 854 | 0.75 855 | 0.25 856 | 0.75 857 | 0.25 858 | 0.75 859 | 0.25 860 | 0.75 861 | 0.25 862 | 0.75 863 | 0.25 864 | 0.75 865 | 0.25 866 | 0.75 867 | 0.25 868 | 0.75 869 | 0.25 870 | 0.75 871 | 0.25 872 | 0.75 873 | 0.25 874 | 0.75 875 | 0.25 876 | 0.75 877 | 0.25 878 | 0.75 879 | 0.25 880 | 0.75 881 | 0.25 882 | 0.75 883 | 0.25 884 | 0.75 885 | 0.25 886 | 0.75 887 | 0.25 888 | 0.75 889 | 0.25 890 | 0.75 891 | 0.25 892 | 0.75 893 | 0.25 894 | 0.75 895 | 0.25 896 | 0.75 897 | 0.25 898 | 0.75 899 | 0.25 900 | 0.75 901 | 0.25 902 | 0.75 903 | 0.25 904 | 0.75 905 | 0.25 906 | 0.75 907 | 0.25 908 | 0.75 909 | 0.25 910 | 0.75 911 | 0.25 912 | 0.75 913 | 0.25 914 | 0.75 915 | 0.25 916 | 0.75 917 | 0.25 918 | 0.75 919 | 0.25 920 | 0.75 921 | 0.25 922 | 0.75 923 | 0.25 924 | 0.75 925 | 0.25 926 | 0.75 927 | 0.25 928 | 0.75 929 | 0.25 930 | 0.75 931 | 0.25 932 | 0.75 933 | 0.25 934 | 0.75 935 | 0.25 936 | 0.75 937 | 0.25 938 | 0.75 939 | 0.25 940 | 0.75 941 | 0.25 942 | 0.75 943 | 0.25 944 | 0.75 945 | 0.25 946 | 0.75 947 | 0.25 948 | 0.75 949 | 0.25 950 | 0.75 951 | 0.25 952 | 0.75 953 | 0.25 954 | 0.75 955 | 0.25 956 | 0.75 957 | 0.25 958 | 0.75 959 | 0.25 960 | 0.75 961 | 0.25 962 | 0.75 963 | 0.25 964 | 0.75 965 | 0.25 966 | 0.75 967 | 0.25 968 | 0.75 969 | 0.25 970 | 0.75 971 | 0.25 972 | 0.75 973 | 0.25 974 | 0.75 975 | 0.25 976 | 0.75 977 | 0.25 978 | 0.75 979 | 0.25 980 | 0.75 981 | 0.25 982 | 0.75 983 | 0.25 984 | 0.75 985 | 0.25 986 | 0.75 987 | 0.25 988 | 0.75 989 | 0.25 990 | 0.75 991 | 0.25 992 | 0.75 993 | 0.25 994 | 0.75 995 | 0.25 996 | 0.75 997 | 0.25 998 | 0.75 999 | 0.25 1000 | 0.75 1001 | 0.25 1002 | 0.75 1003 | 0.25 1004 | 0.75 1005 | 0.25 1006 | 0.75 1007 | 0.25 1008 | 0.75 1009 | 0.25 1010 | 0.75 1011 | 0.25 1012 | 0.75 1013 | 0.25 1014 | 0.75 1015 | 0.25 1016 | 0.75 1017 | 0.25 1018 | 0.75 1019 | 0.25 1020 | 0.75 1021 | 0.25 1022 | 0.75 1023 | 0.25 1024 | 0.75 1025 | 0.25 1026 | 0.75 1027 | 0.25 1028 | 0.75 1029 | 0.25 1030 | 0.75 1031 | 0.25 1032 | 0.75 1033 | 0.25 1034 | 0.75 1035 | 0.25 1036 | 0.75 1037 | 0.25 1038 | 0.75 1039 | 0.25 1040 | 0.75 1041 | -------------------------------------------------------------------------------- /data/sequences/rainflow-seq6.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 0 4 | 1 5 | 0 6 | 1 7 | 0 8 | 1 9 | 0 10 | 1 11 | 0 12 | 1 13 | 0 14 | 1 15 | 0 16 | 1 17 | 0 18 | 1 19 | 0 20 | 1 21 | 0 22 | 1 23 | 0 24 | 1 25 | 0 26 | 1 27 | 0 28 | 1 29 | 0 30 | 1 31 | 0 32 | 1 33 | 0 34 | 1 35 | 0 36 | 1 37 | 0 38 | 1 39 | 0 40 | 1 41 | 0 42 | 1 43 | 0 44 | 1 45 | 0 46 | 1 47 | 0 48 | 1 49 | 0 50 | 1 51 | 0 52 | 1 53 | 0 54 | 1 55 | 0 56 | 1 57 | 0 58 | 1 59 | 0 60 | 1 61 | 0 62 | 1 63 | 0 64 | 1 65 | 0 66 | 1 67 | 0 68 | 1 69 | 0 70 | 1 71 | 0 72 | 1 73 | 0 74 | 1 75 | 0 76 | 1 77 | 0 78 | 1 79 | 0 80 | 1 81 | 0.25 82 | 0.75 83 | 0.25 84 | 0.75 85 | 0.25 86 | 0.75 87 | 0.25 88 | 0.75 89 | 0.25 90 | 0.75 91 | 0.25 92 | 0.75 93 | 0.25 94 | 0.75 95 | 0.25 96 | 0.75 97 | 0.25 98 | 0.75 99 | 0.25 100 | 0.75 101 | 0.25 102 | 0.75 103 | 0.25 104 | 0.75 105 | 0.25 106 | 0.75 107 | 0.25 108 | 0.75 109 | 0.25 110 | 0.75 111 | 0.25 112 | 0.75 113 | 0.25 114 | 0.75 115 | 0.25 116 | 0.75 117 | 0.25 118 | 0.75 119 | 0.25 120 | 0.75 121 | 0.25 122 | 0.75 123 | 0.25 124 | 0.75 125 | 0.25 126 | 0.75 127 | 0.25 128 | 0.75 129 | 0.25 130 | 0.75 131 | 0.25 132 | 0.75 133 | 0.25 134 | 0.75 135 | 0.25 136 | 0.75 137 | 0.25 138 | 0.75 139 | 0.25 140 | 0.75 141 | 0.25 142 | 0.75 143 | 0.25 144 | 0.75 145 | 0.25 146 | 0.75 147 | 0.25 148 | 0.75 149 | 0.25 150 | 0.75 151 | 0.25 152 | 0.75 153 | 0.25 154 | 0.75 155 | 0.25 156 | 0.75 157 | 0.25 158 | 0.75 159 | 0.25 160 | 0.75 161 | 0.25 162 | 0.75 163 | 0.25 164 | 0.75 165 | 0.25 166 | 0.75 167 | 0.25 168 | 0.75 169 | 0.25 170 | 0.75 171 | 0.25 172 | 0.75 173 | 0.25 174 | 0.75 175 | 0.25 176 | 0.75 177 | 0.25 178 | 0.75 179 | 0.25 180 | 0.75 181 | 0 182 | 0.5 183 | 0 184 | 0.5 185 | 0 186 | 0.5 187 | 0 188 | 0.5 189 | 0 190 | 0.5 191 | 0 192 | 0.5 193 | 0 194 | 0.5 195 | 0 196 | 0.5 197 | 0 198 | 0.5 199 | 0 200 | 0.5 201 | 0 202 | 0.5 203 | 0 204 | 0.5 205 | 0 206 | 0.5 207 | 0 208 | 0.5 209 | 0 210 | 0.5 211 | 0 212 | 0.5 213 | 0 214 | 0.5 215 | 0 216 | 0.5 217 | 0 218 | 0.5 219 | 0 220 | 0.5 221 | 0 222 | 0.5 223 | 0 224 | 0.5 225 | 0 226 | 0.5 227 | 0 228 | 0.5 229 | 0 230 | 0.5 231 | 0 232 | 0.5 233 | 0 234 | 0.5 235 | 0 236 | 0.5 237 | 0 238 | 0.5 239 | 0 240 | 0.5 241 | 0 242 | 0.5 243 | 0 244 | 0.5 245 | 0 246 | 0.5 247 | 0 248 | 0.5 249 | 0 250 | 0.5 251 | 0 252 | 0.5 253 | 0 254 | 0.5 255 | 0 256 | 0.5 257 | 0 258 | 0.5 259 | 0 260 | 1 261 | 0.5 262 | 1 263 | 0.5 264 | 1 265 | 0.5 266 | 1 267 | 0.5 268 | 1 269 | 0.5 270 | 1 271 | 0.5 272 | 1 273 | 0.5 274 | 1 275 | 0.5 276 | 1 277 | 0.5 278 | 1 279 | 0.5 280 | 1 281 | 0.5 282 | 1 283 | 0.5 284 | 1 285 | 0.5 286 | 1 287 | 0.5 288 | 1 289 | 0.5 290 | 1 291 | 0.5 292 | 1 293 | 0.5 294 | 1 295 | 0.5 296 | 1 297 | 0.5 298 | 1 299 | 0.5 300 | 1 301 | 0.5 302 | 1 303 | 0.5 304 | 1 305 | 0.5 306 | 1 307 | 0.5 308 | 1 309 | 0.5 310 | 1 311 | 0.5 312 | 1 313 | 0.5 314 | 1 315 | 0.5 316 | 1 317 | 0.5 318 | 1 319 | 0.5 320 | 1 321 | 0.5 322 | 1 323 | 0.5 324 | 1 325 | 0.5 326 | 1 327 | 0.5 328 | 1 329 | 0.5 330 | 1 331 | 0.5 332 | 1 333 | 0.5 334 | 1 335 | 0.5 336 | 1 337 | 0.5 338 | 1 339 | 0.25 340 | 0.75 341 | 0.25 342 | 0.75 343 | 0.25 344 | 0.75 345 | 0.25 346 | 0.75 347 | 0.25 348 | 0.75 349 | 0.25 350 | 0.75 351 | 0.25 352 | 0.75 353 | 0.25 354 | 0.75 355 | 0.25 356 | 0.75 357 | 0.25 358 | 0.75 359 | 0.25 360 | 0.75 361 | 0.25 362 | 0.75 363 | 0.25 364 | 0.75 365 | 0.25 366 | 0.75 367 | 0.25 368 | 0.75 369 | 0.25 370 | 0.75 371 | 0.25 372 | 0.75 373 | 0.25 374 | 0.75 375 | 0.25 376 | 0.75 377 | 0.25 378 | 0.75 379 | 0.25 380 | 0.75 381 | 0.25 382 | 0.75 383 | 0.25 384 | 0.75 385 | 0.25 386 | 0.75 387 | 0.25 388 | 0.75 389 | 0.25 390 | 0.75 391 | 0.25 392 | 0.75 393 | 0.25 394 | 0.75 395 | 0.25 396 | 0.75 397 | 0.25 398 | 0.75 399 | 0.25 400 | 0.75 401 | 0.25 402 | 0.75 403 | 0.25 404 | 0.75 405 | 0.25 406 | 0.75 407 | 0.25 408 | 0.75 409 | 0.25 410 | 0.75 411 | 0.25 412 | 0.75 413 | 0.25 414 | 0.75 415 | 0.25 416 | 0.75 417 | 0.25 418 | 0.75 419 | 0.25 420 | 0.75 421 | 0.25 422 | 0.75 423 | 0.25 424 | 0.75 425 | 0.25 426 | 0.75 427 | 0.25 428 | 0.75 429 | 0.25 430 | 0.75 431 | 0.25 432 | 0.75 433 | 0.25 434 | 0.75 435 | 0.25 436 | 0.75 437 | 0.25 438 | 0.75 439 | 0 440 | 0.333 441 | 0 442 | 0.333 443 | 0 444 | 0.333 445 | 0 446 | 0.333 447 | 0 448 | 0.333 449 | 0 450 | 0.333 451 | 0 452 | 0.333 453 | 0 454 | 0.333 455 | 0 456 | 0.333 457 | 0 458 | 0.333 459 | 0 460 | 0.333 461 | 0 462 | 0.333 463 | 0 464 | 0.333 465 | 0 466 | 0.333 467 | 0 468 | 0.333 469 | 0 470 | 0.333 471 | 0 472 | 0.333 473 | 0 474 | 0.333 475 | 0 476 | 0.333 477 | 0 478 | 0.333 479 | 0 480 | 0.333 481 | 0 482 | 0.333 483 | 0 484 | 0.333 485 | 0 486 | 0.333 487 | 0 488 | 0.333 489 | 0 490 | 0.333 491 | 0 492 | 0.333 493 | 0 494 | 0.333 495 | 0 496 | 0.333 497 | 0 498 | 0.333 499 | 0 500 | 0.333 501 | 0 502 | 0.333 503 | 0 504 | 0.333 505 | 0 506 | 0.333 507 | 0 508 | 0.333 509 | 0 510 | 0.333 511 | 0 512 | 0.333 513 | 0 514 | 0.333 515 | 0 516 | 0.333 517 | 0 518 | 0.666 519 | 0.333 520 | 0.666 521 | 0.333 522 | 0.666 523 | 0.333 524 | 0.666 525 | 0.333 526 | 0.666 527 | 0.333 528 | 0.666 529 | 0.333 530 | 0.666 531 | 0.333 532 | 0.666 533 | 0.333 534 | 0.666 535 | 0.333 536 | 0.666 537 | 0.333 538 | 0.666 539 | 0.333 540 | 0.666 541 | 0.333 542 | 0.666 543 | 0.333 544 | 0.666 545 | 0.333 546 | 0.666 547 | 0.333 548 | 0.666 549 | 0.333 550 | 0.666 551 | 0.333 552 | 0.666 553 | 0.333 554 | 0.666 555 | 0.333 556 | 0.666 557 | 0.333 558 | 0.666 559 | 0.333 560 | 0.666 561 | 0.333 562 | 0.666 563 | 0.333 564 | 0.666 565 | 0.333 566 | 0.666 567 | 0.333 568 | 0.666 569 | 0.333 570 | 0.666 571 | 0.333 572 | 0.666 573 | 0.333 574 | 0.666 575 | 0.333 576 | 0.666 577 | 0.333 578 | 0.666 579 | 0.333 580 | 0.666 581 | 0.333 582 | 0.666 583 | 0.333 584 | 0.666 585 | 0.333 586 | 0.666 587 | 0.333 588 | 0.666 589 | 0.333 590 | 0.666 591 | 0.333 592 | 0.666 593 | 0.333 594 | 0.666 595 | 0.333 596 | 1 597 | 0.666 598 | 1 599 | 0.666 600 | 1 601 | 0.666 602 | 1 603 | 0.666 604 | 1 605 | 0.666 606 | 1 607 | 0.666 608 | 1 609 | 0.666 610 | 1 611 | 0.666 612 | 1 613 | 0.666 614 | 1 615 | 0.666 616 | 1 617 | 0.666 618 | 1 619 | 0.666 620 | 1 621 | 0.666 622 | 1 623 | 0.666 624 | 1 625 | 0.666 626 | 1 627 | 0.666 628 | 1 629 | 0.666 630 | 1 631 | 0.666 632 | 1 633 | 0.666 634 | 1 635 | 0.666 636 | 1 637 | 0.666 638 | 1 639 | 0.666 640 | 1 641 | 0.666 642 | 1 643 | 0.666 644 | 1 645 | 0.666 646 | 1 647 | 0.666 648 | 1 649 | 0.666 650 | 1 651 | 0.666 652 | 1 653 | 0.666 654 | 1 655 | 0.666 656 | 1 657 | 0.666 658 | 1 659 | 0.666 660 | 1 661 | 0.666 662 | 1 663 | 0.666 664 | 1 665 | 0.666 666 | 1 667 | 0.666 668 | 1 669 | 0.666 670 | 1 671 | 0.666 672 | 1 673 | 0.666 674 | 1 675 | 0.25 676 | 0.75 677 | 0.25 678 | 0.75 679 | 0.25 680 | 0.75 681 | 0.25 682 | 0.75 683 | 0.25 684 | 0.75 685 | 0.25 686 | 0.75 687 | 0.25 688 | 0.75 689 | 0.25 690 | 0.75 691 | 0.25 692 | 0.75 693 | 0.25 694 | 0.75 695 | 0.25 696 | 0.75 697 | 0.25 698 | 0.75 699 | 0.25 700 | 0.75 701 | 0.25 702 | 0.75 703 | 0.25 704 | 0.75 705 | 0.25 706 | 0.75 707 | 0.25 708 | 0.75 709 | 0.25 710 | 0.75 711 | 0.25 712 | 0.75 713 | 0.25 714 | 0.75 715 | 0.25 716 | 0.75 717 | 0.25 718 | 0.75 719 | 0.25 720 | 0.75 721 | 0.25 722 | 0.75 723 | 0.25 724 | 0.75 725 | 0.25 726 | 0.75 727 | 0.25 728 | 0.75 729 | 0.25 730 | 0.75 731 | 0.25 732 | 0.75 733 | 0.25 734 | 0.75 735 | 0.25 736 | 0.75 737 | 0.25 738 | 0.75 739 | 0.25 740 | 0.75 741 | 0.25 742 | 0.75 743 | 0.25 744 | 0.75 745 | 0.25 746 | 0.75 747 | 0.25 748 | 0.75 749 | 0.25 750 | 0.75 751 | 0.25 752 | 0.75 753 | 0.25 754 | 0.75 755 | 0.25 756 | 0.75 757 | 0.25 758 | 0.75 759 | 0.25 760 | 0.75 761 | 0.25 762 | 0.75 763 | 0.25 764 | 0.75 765 | 0.25 766 | 0.75 767 | 0.25 768 | 0.75 769 | 0.25 770 | 0.75 771 | 0.25 772 | 0.75 773 | 0.25 774 | 0.75 775 | 0 776 | 0.666 777 | 0 778 | 0.666 779 | 0 780 | 0.666 781 | 0 782 | 0.666 783 | 0 784 | 0.666 785 | 0 786 | 0.666 787 | 0 788 | 0.666 789 | 0 790 | 0.666 791 | 0 792 | 0.666 793 | 0 794 | 0.666 795 | 0 796 | 0.666 797 | 0 798 | 0.666 799 | 0 800 | 0.666 801 | 0 802 | 0.666 803 | 0 804 | 0.666 805 | 0 806 | 0.666 807 | 0 808 | 0.666 809 | 0 810 | 0.666 811 | 0 812 | 0.666 813 | 0 814 | 0.666 815 | 0 816 | 0.666 817 | 0 818 | 0.666 819 | 0 820 | 0.666 821 | 0 822 | 0.666 823 | 0 824 | 0.666 825 | 0 826 | 0.666 827 | 0 828 | 0.666 829 | 0 830 | 0.666 831 | 0 832 | 0.666 833 | 0 834 | 0.666 835 | 0 836 | 0.666 837 | 0 838 | 0.666 839 | 0 840 | 0.666 841 | 0 842 | 0.666 843 | 0 844 | 0.666 845 | 0 846 | 0.666 847 | 0 848 | 0.666 849 | 0 850 | 0.666 851 | 0 852 | 0.666 853 | 0 854 | 1 855 | 0.666 856 | 1 857 | 0.666 858 | 1 859 | 0.666 860 | 1 861 | 0.666 862 | 1 863 | 0.666 864 | 1 865 | 0.666 866 | 1 867 | 0.666 868 | 1 869 | 0.666 870 | 1 871 | 0.666 872 | 1 873 | 0.666 874 | 1 875 | 0.666 876 | 1 877 | 0.666 878 | 1 879 | 0.666 880 | 1 881 | 0.666 882 | 1 883 | 0.666 884 | 1 885 | 0.666 886 | 1 887 | 0.666 888 | 1 889 | 0.666 890 | 1 891 | 0.666 892 | 1 893 | 0.666 894 | 1 895 | 0.666 896 | 1 897 | 0.666 898 | 1 899 | 0.666 900 | 1 901 | 0.666 902 | 1 903 | 0.666 904 | 1 905 | 0.666 906 | 1 907 | 0.666 908 | 1 909 | 0.666 910 | 1 911 | 0.666 912 | 1 913 | 0.666 914 | 1 915 | 0.666 916 | 1 917 | 0.666 918 | 1 919 | 0.666 920 | 1 921 | 0.666 922 | 1 923 | 0.666 924 | 1 925 | 0.666 926 | 1 927 | 0.666 928 | 1 929 | 0.666 930 | 1 931 | 0.666 932 | 1 933 | 0.25 934 | 0.75 935 | 0.25 936 | 0.75 937 | 0.25 938 | 0.75 939 | 0.25 940 | 0.75 941 | 0.25 942 | 0.75 943 | 0.25 944 | 0.75 945 | 0.25 946 | 0.75 947 | 0.25 948 | 0.75 949 | 0.25 950 | 0.75 951 | 0.25 952 | 0.75 953 | 0.25 954 | 0.75 955 | 0.25 956 | 0.75 957 | 0.25 958 | 0.75 959 | 0.25 960 | 0.75 961 | 0.25 962 | 0.75 963 | 0.25 964 | 0.75 965 | 0.25 966 | 0.75 967 | 0.25 968 | 0.75 969 | 0.25 970 | 0.75 971 | 0.25 972 | 0.75 973 | 0.25 974 | 0.75 975 | 0.25 976 | 0.75 977 | 0.25 978 | 0.75 979 | 0.25 980 | 0.75 981 | 0.25 982 | 0.75 983 | 0.25 984 | 0.75 985 | 0.25 986 | 0.75 987 | 0.25 988 | 0.75 989 | 0.25 990 | 0.75 991 | 0.25 992 | 0.75 993 | 0.25 994 | 0.75 995 | 0.25 996 | 0.75 997 | 0.25 998 | 0.75 999 | 0.25 1000 | 0.75 1001 | 0.25 1002 | 0.75 1003 | 0.25 1004 | 0.75 1005 | 0.25 1006 | 0.75 1007 | 0.25 1008 | 0.75 1009 | 0.25 1010 | 0.75 1011 | 0.25 1012 | 0.75 1013 | 0.25 1014 | 0.75 1015 | 0.25 1016 | 0.75 1017 | 0.25 1018 | 0.75 1019 | 0.25 1020 | 0.75 1021 | 0.25 1022 | 0.75 1023 | 0.25 1024 | 0.75 1025 | 0.25 1026 | 0.75 1027 | 0.25 1028 | 0.75 1029 | 0.25 1030 | 0.75 1031 | 0.25 1032 | 0.75 1033 | 0.25 1034 | 0.75 1035 | 0.25 1036 | 0.75 1037 | 0.25 1038 | 0.75 1039 | 0.25 1040 | 0.75 1041 | 0.25 1042 | 0.75 1043 | 0.25 1044 | 0.75 1045 | 0.25 1046 | 0.75 1047 | 0.25 1048 | 0.75 1049 | 0.25 1050 | 0.75 1051 | 0.25 1052 | 0.75 1053 | 0.25 1054 | 0.75 1055 | 0.25 1056 | 0.75 1057 | 0.25 1058 | 0.75 1059 | 0.25 1060 | 0.75 1061 | 0.25 1062 | 0.75 1063 | 0.25 1064 | 0.75 1065 | 0.25 1066 | 0.75 1067 | 0.25 1068 | 0.75 1069 | 0.25 1070 | 0.75 1071 | 0.25 1072 | 0.75 1073 | 0.25 1074 | 0.75 1075 | 0.25 1076 | 0.75 1077 | 0.25 1078 | 0.75 1079 | 0.25 1080 | 0.75 1081 | 0.25 1082 | 0.75 1083 | 0.25 1084 | 0.75 1085 | 0.25 1086 | 0.75 1087 | 0.25 1088 | 0.75 1089 | 0.25 1090 | 0.75 1091 | 0.25 1092 | 0.75 1093 | 0.25 1094 | 0.75 1095 | 0.25 1096 | 0.75 1097 | 0.25 1098 | 0.75 1099 | 0.25 1100 | 0.75 1101 | 0.25 1102 | 0.75 1103 | 0.25 1104 | 0.75 1105 | 0.25 1106 | 0.75 1107 | 0.25 1108 | 0.75 1109 | 0.25 1110 | 0.75 1111 | 0.25 1112 | 0.75 1113 | 0.25 1114 | 0.75 1115 | 0.25 1116 | 0.75 1117 | 0.25 1118 | 0.75 1119 | 0.25 1120 | 0.75 1121 | 0.25 1122 | 0.75 1123 | 0.25 1124 | 0.75 1125 | 0.25 1126 | 0.75 1127 | 0.25 1128 | 0.75 1129 | 0.25 1130 | 0.75 1131 | 0.25 1132 | 0.75 1133 | 0.25 1134 | 0.75 1135 | 0.25 1136 | 0.75 1137 | 0.25 1138 | 0.75 1139 | 0.25 1140 | 0.75 1141 | 0.25 1142 | 0.75 1143 | 0.25 1144 | 0.75 1145 | 0.25 1146 | 0.75 1147 | 0.25 1148 | 0.75 1149 | 0.25 1150 | 0.75 1151 | 0.25 1152 | 0.75 1153 | 0.25 1154 | 0.75 1155 | 0.25 1156 | 0.75 1157 | 0.25 1158 | 0.75 1159 | 0.25 1160 | 0.75 1161 | 0.25 1162 | 0.75 1163 | 0.25 1164 | 0.75 1165 | 0.25 1166 | 0.75 1167 | 0.25 1168 | 0.75 1169 | 0.25 1170 | 0.75 1171 | 0.25 1172 | 0.75 1173 | 0.25 1174 | 0.75 1175 | 0.25 1176 | 0.75 1177 | 0.25 1178 | 0.75 1179 | 0.25 1180 | 0.75 1181 | 0.25 1182 | 0.75 1183 | 0.25 1184 | 0.75 1185 | 0.25 1186 | 0.75 1187 | 0.25 1188 | 0.75 1189 | 0.25 1190 | 0.75 1191 | 0.25 1192 | 0.75 1193 | 0.25 1194 | 0.75 1195 | 0.25 1196 | 0.75 1197 | 0.25 1198 | 0.75 1199 | 0.25 1200 | 0.75 1201 | 0.25 1202 | 0.75 1203 | 0.25 1204 | 0.75 1205 | 0.25 1206 | 0.75 1207 | 0.25 1208 | 0.75 1209 | 0.25 1210 | 0.75 1211 | 0.25 1212 | 0.75 1213 | 0.25 1214 | 0.75 1215 | 0.25 1216 | 0.75 1217 | 0.25 1218 | 0.75 1219 | 0.25 1220 | 0.75 1221 | 0.25 1222 | 0.75 1223 | 0.25 1224 | 0.75 1225 | 0.25 1226 | 0.75 1227 | 0.25 1228 | 0.75 1229 | 0.25 1230 | 0.75 1231 | 0.25 1232 | 0.75 1233 | -------------------------------------------------------------------------------- /src/list.rs: -------------------------------------------------------------------------------- 1 | use fatigue::{beta, grow, material}; 2 | 3 | static HIGHLIGHTS: &'static str = " 4 | * **Sequence filtering** Performs sequence reordering, 5 | turning-point, dead-band, rise-fall filtering and rain-flow 6 | counting. The filtered sequences may be written to a file. 7 | 8 | * **Inbuilt data** Comes with a selection of beta factors, 9 | material data and crack growth models. 10 | 11 | * **Calculated parameters** Calculates additional parameters for 12 | characterising the state of the crack tip so that better 13 | crack growth equations can be developed based on the most 14 | applicable parameters for a material. 15 | 16 | * **Optimisation** Optimises the crack growth model parameters to 17 | minimise the difference between predicted and measured crack 18 | growth. The measured data need not be for the entire history 19 | i.e., one or more fractographic measurements of the width of 20 | a block. 21 | 22 | * **Image generation** Generates a pseudo fractographic image of the fracture 23 | surface to see how easy it is to identify the individual 24 | blocks. 25 | "; 26 | 27 | static UNITS: &'static str = "The internal da/dN data are all in units for stress intensity of (MPa 28 | m^0.5) and growth in (m). Most beta equations use an applied far field 29 | stress which is in units of (MPa). Thus the scaling factor in units of 30 | (MPa) will generally convert the sequence into the correct 31 | units. However, the compact-tension beta factor compact_tada73 uses 32 | applied load not stress and is in units of load of (MN) and coupon 33 | dimensions are in (m). The width and depth will need to be set for the 34 | compact-tension beta function otherwise **easiGrow** will assume an 35 | infinite plate and the crack will not grow. 36 | "; 37 | 38 | /// Prints out lists of data. Sort of an extended help. 39 | pub fn print_list() { 40 | // List of the valid names for producing output 41 | let output = [ 42 | ("block", "block number"), 43 | ("line", "line number"), 44 | ("a/c", "crack aspect ratio"), 45 | ("a/d", "cracked fraction of forward distance"), 46 | ("c/b", "cracked fraction of sideways distance"), 47 | ("k", "current cycle K"), 48 | ("dk", "current cycle dK"), 49 | ("r", "current cycle R"), 50 | ("beta_a", "beta factor at a"), 51 | ("beta_c", "beta factor at c"), 52 | ("a", "forward distance of crack from origin"), 53 | ("c", "sideways distance of crack from origin"), 54 | ("da", "crack growth increment at a"), 55 | ("dc", "crack growth increment at c"), 56 | ("mono", "largest monotonic plastic zone "), 57 | ("cyclic", "largest cyclic plastic zone "), 58 | ( 59 | "a/mono", 60 | "ratio of crack length to monotonic plastic zone size", 61 | ), 62 | ( 63 | "a/cyclic", 64 | "ratio of crack length to cyclic plastic zone size", 65 | ), 66 | ( 67 | "mono/da", 68 | "ratio of current cyclic plastic zone to current da", 69 | ), 70 | ( 71 | "cyclic/da", 72 | "ratio of current cyclic plastic zone to current da", 73 | ), 74 | ("peak", "scaled peak stress of current cycle"), 75 | ("valley", "scaled valley stress of current cycle"), 76 | ]; 77 | 78 | let formats = [ 79 | ( 80 | "Crack file", 81 | "The crack growth file is in the following format: 82 | 83 | 84 | ... 85 | 86 | or 87 | 88 | 89 | ... 90 | 91 | or 92 | 93 | 94 | ... 95 | 96 | Blank lines in the file indicate non-contiguous measurements. If 97 | or are missing the program will assume the readings are 98 | one block apart with each block measured at line 0. Use the same 99 | format for the entire file. Where represents the corresponding 100 | line no. (starting at 0) of the sequence file, and is the 101 | no. of the block at that crack depth. Strictly speaking, the actual 102 | block numbers are not used by easigrow with only the difference between 103 | the block numbers in contiguous measurements used. Easigrow only 104 | matches the average crack growth rate using: 105 | 106 | rate = log(growth between measurements) / log(no. of blocks between measurements). 107 | ", 108 | ), 109 | ( 110 | "Optimise file", 111 | "Each line in the optimise file is a list of easigrow command lines 112 | (without the 'easigrow' command) with each line containing the easigrow 113 | options that will best reproduce the crack growth calculation for the 114 | associated crack growth curve that it is trying to match. Note: Only 115 | the material model specified by the main command line that invokes the 116 | optimisation will be used for all crack predictions, since those will 117 | be the parameters that are optimised. Any other material 118 | specifications will be ignored. 119 | 120 | The format of the optimisation file is: 121 | 122 | ... --crack 123 | ... --crack 124 | ... 125 | ", 126 | ), 127 | ( 128 | "Beta file", 129 | "All lines beginning with a # are treated as a comment and ignored. The 130 | format of the beta file is 131 | 132 | # Comment describing the contents of the file 133 | a/d beta 134 | ... 135 | ", 136 | ), 137 | ( 138 | "Dadn file", 139 | "All lines beginning with a # are treated as a comment and ignored. The 140 | format of the file is: 141 | 142 | # Comment describing the contents of the file 143 | r1 r2 .... 144 | dadn1 deltaK1_r1 deltaK1_r2 .... 145 | dadn2 deltaK2_r1 deltaK2_r2 .... 146 | ... 147 | ", 148 | ), 149 | ]; 150 | 151 | let biblio = [ 152 | ["[Newman79]", "J. C. Newman , Jr. and I. S. Raju 153 | Analyses of surface cracks in finite plates under tension or bending loads 154 | NASA Technical Paper 1578 155 | December 1979"], 156 | 157 | ["[Newman81]", " J. C. Newman Jr. and I. S. Raju 158 | Stress intensity factor equations for cracks 159 | in three-dimensional finite bodies, 160 | NASA Technical Memorandum 83299, 1981 p 1--49."], 161 | 162 | ["[Newman81]", " J. C. Newman Jr. and I. S. Raju 163 | Stress-intensity factor equations for 164 | cracks in three-dimensional finite bodies 165 | subjected to tension and bending loads, 166 | NASA Technical Memorandum 85739, 1984."], 167 | 168 | ["[Anderson05]", "T. L. Anderson 169 | Fracture Mechanics - Fundamentals and Applications 170 | Taylor and Francis 3rd Edition 2005"], 171 | 172 | ["[Tada73]", "H. Tada, P. C. Paris and G. R. Irwin 173 | The Stress Analysis of Cracks Handbook 174 | 1973"], 175 | 176 | ["[Murakami87]", "Y. Murakami 177 | Stress Intensity Factors Handbook. Vol 2 178 | Pergamon Press, Oxford, , 1987"], 179 | 180 | ["[Murakami87a]", "Yukitaka Murakami and Hideto Tsuru 181 | Stress Intensity factor equations for a semi-elliptical surface crack in a shaft under bending 182 | 1986"], 183 | 184 | ["[Schwarmann86]", "L. Schwarmann 185 | Material Data of High-Strength Aluminium Alloys for Durability Evaluation of Structures 186 | Aluminium-Verlag 1986 187 | Note: The data from this report has been converted from mm/cycle to m/cyclic by factoring cf by 1e3."], 188 | 189 | ["[Fedderson66]", " 190 | Taken from Damage Tolerant Design handbook from AFGROW documentation."], 191 | 192 | ["[Kujawski01]", "Daniel Kujawski, 193 | A fatigue crack driving force parameter with load ratio effects 194 | International Journal of Fatigue, Vol 23, S239-S246, 2001"], 195 | 196 | ["[Walker70]", "K. Walker 197 | The effect of stress ratio during crack propagation and fatigue for {2024-T3} and {7075-T6} aluminum 198 | Effects of Environment and Complex Load History for Fatigue Life, 199 | American Society for Testing and Materials,Special Technical Publication 462, 1970"], 200 | 201 | ["[Jones12]", "Jones, R., Molent, L. & Walker, K. 202 | Fatigue crack growth in a diverse 203 | range of materials, International Journal of Fatigue Vol. 40,pages 43--50, 2012"], 204 | 205 | ["[Hartman70]", "A. Hartman and J. Schijve 206 | The effects of environment and load frequency on the 207 | crack propagation law for macro fatigue crack growth in aluminum alloys, 208 | Engineering Fracture Mechanics, Vol. 1(4), 1970"], 209 | 210 | ["[Shin04]", "C.S. Shin and C. Q. CAI 211 | Experimental and finite element analyses on stress intensity 212 | factors of an elliptical surface crack in a circular shaft under 213 | tension and bending, 214 | International Journal of Fracture 129: 239–264, 2004."], 215 | 216 | ["[Forman05]", "R. G. Forman, V. Shivakumar, J. W. Cardinal , L. C. Williams and P. C. McKeighan 217 | Fatigue Crack Growth Database for Damage Tolerance Analysis, 218 | DOT/FAA/AR-05/15, 2005."], 219 | ]; 220 | 221 | // Set up a new counter to automatically label the section headers 222 | let mut header = Counter::new(); 223 | 224 | header.section("Program Highlights"); 225 | print!("{}", HIGHLIGHTS); 226 | 227 | header.section("Units"); 228 | print!("{}", UNITS); 229 | 230 | header.section("Output Parameters"); 231 | 232 | for &(abbrev, descrip) in &output { 233 | println!("{:20} {}", abbrev, descrip); 234 | } 235 | 236 | header.section("Beta Models"); 237 | // List of the beta equations that are implemented 238 | let betas = beta::get_all_betas(&grow::Component { 239 | forward: 0.0, 240 | sideways: 0.0, 241 | radius: 0.0, 242 | material: material::Properties { 243 | yield_stress: 0.0, 244 | k1c: 0.0, 245 | youngs_modulus: 0.0, 246 | }, 247 | }); 248 | for beta in &betas { 249 | println!( 250 | "{:20} {:30} {} {}", 251 | beta.name, 252 | beta.args.to_string(), 253 | beta.summary, 254 | beta.cite 255 | ); 256 | } 257 | 258 | header.section("Cycle Counting Models"); 259 | 260 | println!("Crack growth is calculated for each cycle. The cyles can 261 | be input directly or extracted from a sequence. The way the cycles are 262 | determined affects the growth. The methods for extracting cycles from 263 | a sequence are:"); 264 | 265 | println!( 266 | " 267 | rainflow Crack growth is calculated from rainflow cycles i.e. the 268 | stress intensity range comes from the range of the rainflow 269 | cycles. Note this has a slight re-ordering effect that may upset 270 | the order of any image plots created. 271 | 272 | tension Crack growth calculated from tension part of cycle i.e. from a valley 273 | to the next peak. The striation pattern follows these tension cycles. 274 | "); 275 | 276 | header.section("da/dN data"); 277 | println!( 278 | "The da/dN model consists of EQUATION:material where the 279 | equation variable specifies the name of the da/dN equation and is 280 | one of [nasgro, paris, forman, walker, burchill, hartman, white, file] 281 | The material variable specifies the name of the parameters to use for 282 | that equation. If the values are given in --parameters they will 283 | be used instead of the standard library values.\n 284 | " 285 | ); 286 | 287 | println!("{:35} {:20} Coefficients", "Name", "Ref."); 288 | let materials = material::get_all_dadns(); 289 | for mat in materials.iter() { 290 | print!("{:35} {:20} ", mat.name, mat.cite); 291 | for num in &mat.eqn.variables() { 292 | print!("{:.5e} ", num) 293 | } 294 | println!(); 295 | } 296 | println!( 297 | "{:35} {:20} {:30}", 298 | "file:FILE", " ", "Read FILE of tabular dadn data." 299 | ); 300 | 301 | header.section("File formats"); 302 | 303 | for &(file, form) in &formats { 304 | header.subsection(file); 305 | println!("{}", form); 306 | } 307 | 308 | header.section("References"); 309 | 310 | for bib in &biblio { 311 | println!("{} {}\n", bib[0], bib[1]); 312 | } 313 | 314 | println!(); 315 | } 316 | 317 | struct Counter { 318 | section: usize, 319 | subsection: usize, 320 | } 321 | 322 | impl Counter { 323 | fn new() -> Counter { 324 | Counter { 325 | section: 0, 326 | subsection: 0, 327 | } 328 | } 329 | 330 | // print as a header 331 | fn section(&mut self, head: &str) { 332 | self.section += 1; 333 | let header = format!("{}. {}", self.section, head); 334 | println!("\n{}", header); 335 | // Underline 336 | for _ in 0..header.len() { 337 | print!("="); 338 | } 339 | println!("\n"); 340 | } 341 | 342 | fn subsection(&mut self, head: &str) { 343 | self.subsection += 1; 344 | let header = format!("{}.{}. {}", self.section, self.subsection, head); 345 | println!("{}", header); 346 | // Underline 347 | for _ in 0..header.len() { 348 | print!("-"); 349 | } 350 | println!("\n"); 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /data/sequences/rainflow-seq3.txt: -------------------------------------------------------------------------------- 1 | 0.25 2 | 1.15 3 | 0.35 4 | 1.25 5 | 0.25 6 | 1.15 7 | 0.35 8 | 1.25 9 | 0.25 10 | 1.15 11 | 0.35 12 | 1.25 13 | 0.25 14 | 1.15 15 | 0.35 16 | 1.25 17 | 0.25 18 | 1.15 19 | 0.35 20 | 1.25 21 | 0.25 22 | 1.15 23 | 0.35 24 | 1.25 25 | 0.25 26 | 1.15 27 | 0.35 28 | 1.25 29 | 0.25 30 | 1.15 31 | 0.35 32 | 1.25 33 | 0.25 34 | 1.15 35 | 0.35 36 | 1.25 37 | 0.25 38 | 1.15 39 | 0.35 40 | 1.25 41 | 0.25 42 | 1.15 43 | 0.35 44 | 1.25 45 | 0.25 46 | 1.15 47 | 0.35 48 | 1.25 49 | 0.25 50 | 1.15 51 | 0.35 52 | 1.25 53 | 0.25 54 | 1.15 55 | 0.35 56 | 1.25 57 | 0.25 58 | 1.15 59 | 0.35 60 | 1.25 61 | 0.25 62 | 1.15 63 | 0.35 64 | 1.25 65 | 0.25 66 | 1.15 67 | 0.35 68 | 1.25 69 | 0.25 70 | 1.15 71 | 0.35 72 | 1.25 73 | 0.25 74 | 1.15 75 | 0.35 76 | 1.25 77 | 0.25 78 | 1.15 79 | 0.35 80 | 1.25 81 | 0.25 82 | 1.15 83 | 0.35 84 | 1.25 85 | 0.25 86 | 1.15 87 | 0.35 88 | 1.25 89 | 0.25 90 | 1.15 91 | 0.35 92 | 1.25 93 | 0.25 94 | 1.15 95 | 0.35 96 | 1.25 97 | 0.25 98 | 1.15 99 | 0.35 100 | 1.25 101 | 0.25 102 | 1.15 103 | 0.35 104 | 1.25 105 | 0.25 106 | 1.15 107 | 0.35 108 | 1.25 109 | 0.25 110 | 1.15 111 | 0.35 112 | 1.25 113 | 0.25 114 | 1.15 115 | 0.35 116 | 1.25 117 | 0.25 118 | 1.15 119 | 0.35 120 | 1.25 121 | 0.25 122 | 1.15 123 | 0.35 124 | 1.25 125 | 0.25 126 | 1.15 127 | 0.35 128 | 1.25 129 | 0.25 130 | 1.15 131 | 0.35 132 | 1.25 133 | 0.25 134 | 1.15 135 | 0.35 136 | 1.25 137 | 0.25 138 | 1.15 139 | 0.35 140 | 1.25 141 | 0.25 142 | 1.15 143 | 0.35 144 | 1.25 145 | 0.25 146 | 1.15 147 | 0.35 148 | 1.25 149 | 0.25 150 | 1.15 151 | 0.35 152 | 1.25 153 | 0.25 154 | 1.15 155 | 0.35 156 | 1.25 157 | 0.25 158 | 1.15 159 | 0.35 160 | 1.25 161 | 0.5 162 | 1 163 | 0.5 164 | 1 165 | 0.5 166 | 1 167 | 0.5 168 | 1 169 | 0.5 170 | 1 171 | 0.5 172 | 1 173 | 0.5 174 | 1 175 | 0.5 176 | 1 177 | 0.5 178 | 1 179 | 0.5 180 | 1 181 | 0.5 182 | 1 183 | 0.5 184 | 1 185 | 0.5 186 | 1 187 | 0.5 188 | 1 189 | 0.5 190 | 1 191 | 0.5 192 | 1 193 | 0.5 194 | 1 195 | 0.5 196 | 1 197 | 0.5 198 | 1 199 | 0.5 200 | 1 201 | 0.5 202 | 1 203 | 0.5 204 | 1 205 | 0.5 206 | 1 207 | 0.5 208 | 1 209 | 0.5 210 | 1 211 | 0.5 212 | 1 213 | 0.5 214 | 1 215 | 0.5 216 | 1 217 | 0.5 218 | 1 219 | 0.5 220 | 1 221 | 0.5 222 | 1 223 | 0.5 224 | 1 225 | 0.5 226 | 1 227 | 0.5 228 | 1 229 | 0.5 230 | 1 231 | 0.5 232 | 1 233 | 0.5 234 | 1 235 | 0.5 236 | 1 237 | 0.5 238 | 1 239 | 0.5 240 | 1 241 | 0.5 242 | 1 243 | 0.5 244 | 1 245 | 0.5 246 | 1 247 | 0.5 248 | 1 249 | 0.5 250 | 1 251 | 0.5 252 | 1 253 | 0.5 254 | 1 255 | 0.5 256 | 1 257 | 0.5 258 | 1 259 | 0.5 260 | 1 261 | 0.35 262 | 1.15 263 | 0.35 264 | 1.15 265 | 0.35 266 | 1.15 267 | 0.35 268 | 1.15 269 | 0.35 270 | 1.15 271 | 0.35 272 | 1.15 273 | 0.35 274 | 1.15 275 | 0.35 276 | 1.15 277 | 0.35 278 | 1.15 279 | 0.35 280 | 1.15 281 | 0.35 282 | 1.15 283 | 0.35 284 | 1.15 285 | 0.35 286 | 1.15 287 | 0.35 288 | 1.15 289 | 0.35 290 | 1.15 291 | 0.35 292 | 1.15 293 | 0.35 294 | 1.15 295 | 0.35 296 | 1.15 297 | 0.35 298 | 1.15 299 | 0.35 300 | 1.15 301 | 0.35 302 | 1.15 303 | 0.35 304 | 1.15 305 | 0.35 306 | 1.15 307 | 0.35 308 | 1.15 309 | 0.35 310 | 1.15 311 | 0.35 312 | 1.15 313 | 0.35 314 | 1.15 315 | 0.35 316 | 1.15 317 | 0.35 318 | 1.15 319 | 0.35 320 | 1.15 321 | 0.35 322 | 1.15 323 | 0.35 324 | 1.15 325 | 0.35 326 | 1.15 327 | 0.35 328 | 1.15 329 | 0.35 330 | 1.15 331 | 0.35 332 | 1.15 333 | 0.35 334 | 1.15 335 | 0.35 336 | 1.15 337 | 0.35 338 | 1.15 339 | 0.35 340 | 1.15 341 | 0.25 342 | 1.25 343 | 0.25 344 | 1.25 345 | 0.25 346 | 1.25 347 | 0.25 348 | 1.25 349 | 0.25 350 | 1.25 351 | 0.25 352 | 1.25 353 | 0.25 354 | 1.25 355 | 0.25 356 | 1.25 357 | 0.25 358 | 1.25 359 | 0.25 360 | 1.25 361 | 0.25 362 | 1.25 363 | 0.25 364 | 1.25 365 | 0.25 366 | 1.25 367 | 0.25 368 | 1.25 369 | 0.25 370 | 1.25 371 | 0.25 372 | 1.25 373 | 0.25 374 | 1.25 375 | 0.25 376 | 1.25 377 | 0.25 378 | 1.25 379 | 0.25 380 | 1.25 381 | 0.25 382 | 1.25 383 | 0.25 384 | 1.25 385 | 0.25 386 | 1.25 387 | 0.25 388 | 1.25 389 | 0.25 390 | 1.25 391 | 0.25 392 | 1.25 393 | 0.25 394 | 1.25 395 | 0.25 396 | 1.25 397 | 0.25 398 | 1.25 399 | 0.25 400 | 1.25 401 | 0.25 402 | 1.25 403 | 0.25 404 | 1.25 405 | 0.25 406 | 1.25 407 | 0.25 408 | 1.25 409 | 0.25 410 | 1.25 411 | 0.25 412 | 1.25 413 | 0.25 414 | 1.25 415 | 0.25 416 | 1.25 417 | 0.25 418 | 1.25 419 | 0.25 420 | 1.25 421 | 0.5 422 | 1 423 | 0.5 424 | 1 425 | 0.5 426 | 1 427 | 0.5 428 | 1 429 | 0.5 430 | 1 431 | 0.5 432 | 1 433 | 0.5 434 | 1 435 | 0.5 436 | 1 437 | 0.5 438 | 1 439 | 0.5 440 | 1 441 | 0.5 442 | 1 443 | 0.5 444 | 1 445 | 0.5 446 | 1 447 | 0.5 448 | 1 449 | 0.5 450 | 1 451 | 0.5 452 | 1 453 | 0.5 454 | 1 455 | 0.5 456 | 1 457 | 0.5 458 | 1 459 | 0.5 460 | 1 461 | 0.5 462 | 1 463 | 0.5 464 | 1 465 | 0.5 466 | 1 467 | 0.5 468 | 1 469 | 0.5 470 | 1 471 | 0.5 472 | 1 473 | 0.5 474 | 1 475 | 0.5 476 | 1 477 | 0.5 478 | 1 479 | 0.5 480 | 1 481 | 0.5 482 | 1 483 | 0.5 484 | 1 485 | 0.5 486 | 1 487 | 0.5 488 | 1 489 | 0.5 490 | 1 491 | 0.5 492 | 1 493 | 0.5 494 | 1 495 | 0.5 496 | 1 497 | 0.5 498 | 1 499 | 0.5 500 | 1 501 | 0.5 502 | 1 503 | 0.5 504 | 1 505 | 0.5 506 | 1 507 | 0.5 508 | 1 509 | 0.5 510 | 1 511 | 0.5 512 | 1 513 | 0.5 514 | 1 515 | 0.5 516 | 1 517 | 0.5 518 | 1 519 | 0.5 520 | 1 521 | 0.25 522 | 1.15 523 | 0.25 524 | 1.15 525 | 0.25 526 | 1.15 527 | 0.25 528 | 1.15 529 | 0.25 530 | 1.15 531 | 0.25 532 | 1.15 533 | 0.25 534 | 1.15 535 | 0.25 536 | 1.15 537 | 0.25 538 | 1.15 539 | 0.25 540 | 1.15 541 | 0.25 542 | 1.15 543 | 0.25 544 | 1.15 545 | 0.25 546 | 1.15 547 | 0.25 548 | 1.15 549 | 0.25 550 | 1.15 551 | 0.25 552 | 1.15 553 | 0.25 554 | 1.15 555 | 0.25 556 | 1.15 557 | 0.25 558 | 1.15 559 | 0.25 560 | 1.15 561 | 0.25 562 | 1.15 563 | 0.25 564 | 1.15 565 | 0.25 566 | 1.15 567 | 0.25 568 | 1.15 569 | 0.25 570 | 1.15 571 | 0.25 572 | 1.15 573 | 0.25 574 | 1.15 575 | 0.25 576 | 1.15 577 | 0.25 578 | 1.15 579 | 0.25 580 | 1.15 581 | 0.25 582 | 1.15 583 | 0.25 584 | 1.15 585 | 0.25 586 | 1.15 587 | 0.25 588 | 1.15 589 | 0.25 590 | 1.15 591 | 0.25 592 | 1.15 593 | 0.25 594 | 1.15 595 | 0.25 596 | 1.15 597 | 0.25 598 | 1.15 599 | 0.25 600 | 1.15 601 | 0.35 602 | 1.25 603 | 0.35 604 | 1.25 605 | 0.35 606 | 1.25 607 | 0.35 608 | 1.25 609 | 0.35 610 | 1.25 611 | 0.35 612 | 1.25 613 | 0.35 614 | 1.25 615 | 0.35 616 | 1.25 617 | 0.35 618 | 1.25 619 | 0.35 620 | 1.25 621 | 0.35 622 | 1.25 623 | 0.35 624 | 1.25 625 | 0.35 626 | 1.25 627 | 0.35 628 | 1.25 629 | 0.35 630 | 1.25 631 | 0.35 632 | 1.25 633 | 0.35 634 | 1.25 635 | 0.35 636 | 1.25 637 | 0.35 638 | 1.25 639 | 0.35 640 | 1.25 641 | 0.35 642 | 1.25 643 | 0.35 644 | 1.25 645 | 0.35 646 | 1.25 647 | 0.35 648 | 1.25 649 | 0.35 650 | 1.25 651 | 0.35 652 | 1.25 653 | 0.35 654 | 1.25 655 | 0.35 656 | 1.25 657 | 0.35 658 | 1.25 659 | 0.35 660 | 1.25 661 | 0.35 662 | 1.25 663 | 0.35 664 | 1.25 665 | 0.35 666 | 1.25 667 | 0.35 668 | 1.25 669 | 0.35 670 | 1.25 671 | 0.35 672 | 1.25 673 | 0.35 674 | 1.25 675 | 0.35 676 | 1.25 677 | 0.35 678 | 1.25 679 | 0.35 680 | 1.25 681 | 0.5 682 | 1 683 | 0.5 684 | 1 685 | 0.5 686 | 1 687 | 0.5 688 | 1 689 | 0.5 690 | 1 691 | 0.5 692 | 1 693 | 0.5 694 | 1 695 | 0.5 696 | 1 697 | 0.5 698 | 1 699 | 0.5 700 | 1 701 | 0.5 702 | 1 703 | 0.5 704 | 1 705 | 0.5 706 | 1 707 | 0.5 708 | 1 709 | 0.5 710 | 1 711 | 0.5 712 | 1 713 | 0.5 714 | 1 715 | 0.5 716 | 1 717 | 0.5 718 | 1 719 | 0.5 720 | 1 721 | 0.5 722 | 1 723 | 0.5 724 | 1 725 | 0.5 726 | 1 727 | 0.5 728 | 1 729 | 0.5 730 | 1 731 | 0.5 732 | 1 733 | 0.5 734 | 1 735 | 0.5 736 | 1 737 | 0.5 738 | 1 739 | 0.5 740 | 1 741 | 0.5 742 | 1 743 | 0.5 744 | 1 745 | 0.5 746 | 1 747 | 0.5 748 | 1 749 | 0.5 750 | 1 751 | 0.5 752 | 1 753 | 0.5 754 | 1 755 | 0.5 756 | 1 757 | 0.5 758 | 1 759 | 0.5 760 | 1 761 | 0.5 762 | 1 763 | 0.5 764 | 1 765 | 0.5 766 | 1 767 | 0.5 768 | 1 769 | 0.5 770 | 1 771 | 0.5 772 | 1 773 | 0.5 774 | 1 775 | 0.5 776 | 1 777 | 0.5 778 | 1 779 | 0.5 780 | 1 781 | 0.25 782 | 1.25 783 | 0.35 784 | 1.15 785 | 0.25 786 | 1.25 787 | 0.35 788 | 1.15 789 | 0.25 790 | 1.25 791 | 0.35 792 | 1.15 793 | 0.25 794 | 1.25 795 | 0.35 796 | 1.15 797 | 0.25 798 | 1.25 799 | 0.35 800 | 1.15 801 | 0.25 802 | 1.25 803 | 0.35 804 | 1.15 805 | 0.25 806 | 1.25 807 | 0.35 808 | 1.15 809 | 0.25 810 | 1.25 811 | 0.35 812 | 1.15 813 | 0.25 814 | 1.25 815 | 0.35 816 | 1.15 817 | 0.25 818 | 1.25 819 | 0.35 820 | 1.15 821 | 0.25 822 | 1.25 823 | 0.35 824 | 1.15 825 | 0.25 826 | 1.25 827 | 0.35 828 | 1.15 829 | 0.25 830 | 1.25 831 | 0.35 832 | 1.15 833 | 0.25 834 | 1.25 835 | 0.35 836 | 1.15 837 | 0.25 838 | 1.25 839 | 0.35 840 | 1.15 841 | 0.25 842 | 1.25 843 | 0.35 844 | 1.15 845 | 0.25 846 | 1.25 847 | 0.35 848 | 1.15 849 | 0.25 850 | 1.25 851 | 0.35 852 | 1.15 853 | 0.25 854 | 1.25 855 | 0.35 856 | 1.15 857 | 0.25 858 | 1.25 859 | 0.35 860 | 1.15 861 | 0.25 862 | 1.25 863 | 0.35 864 | 1.15 865 | 0.25 866 | 1.25 867 | 0.35 868 | 1.15 869 | 0.25 870 | 1.25 871 | 0.35 872 | 1.15 873 | 0.25 874 | 1.25 875 | 0.35 876 | 1.15 877 | 0.25 878 | 1.25 879 | 0.35 880 | 1.15 881 | 0.25 882 | 1.25 883 | 0.35 884 | 1.15 885 | 0.25 886 | 1.25 887 | 0.35 888 | 1.15 889 | 0.25 890 | 1.25 891 | 0.35 892 | 1.15 893 | 0.25 894 | 1.25 895 | 0.35 896 | 1.15 897 | 0.25 898 | 1.25 899 | 0.35 900 | 1.15 901 | 0.25 902 | 1.25 903 | 0.35 904 | 1.15 905 | 0.25 906 | 1.25 907 | 0.35 908 | 1.15 909 | 0.25 910 | 1.25 911 | 0.35 912 | 1.15 913 | 0.25 914 | 1.25 915 | 0.35 916 | 1.15 917 | 0.25 918 | 1.25 919 | 0.35 920 | 1.15 921 | 0.25 922 | 1.25 923 | 0.35 924 | 1.15 925 | 0.25 926 | 1.25 927 | 0.35 928 | 1.15 929 | 0.25 930 | 1.25 931 | 0.35 932 | 1.15 933 | 0.25 934 | 1.25 935 | 0.35 936 | 1.15 937 | 0.25 938 | 1.25 939 | 0.35 940 | 1.15 941 | 0.5 942 | 1 943 | 0.5 944 | 1 945 | 0.5 946 | 1 947 | 0.5 948 | 1 949 | 0.5 950 | 1 951 | 0.5 952 | 1 953 | 0.5 954 | 1 955 | 0.5 956 | 1 957 | 0.5 958 | 1 959 | 0.5 960 | 1 961 | 0.5 962 | 1 963 | 0.5 964 | 1 965 | 0.5 966 | 1 967 | 0.5 968 | 1 969 | 0.5 970 | 1 971 | 0.5 972 | 1 973 | 0.5 974 | 1 975 | 0.5 976 | 1 977 | 0.5 978 | 1 979 | 0.5 980 | 1 981 | 0.5 982 | 1 983 | 0.5 984 | 1 985 | 0.5 986 | 1 987 | 0.5 988 | 1 989 | 0.5 990 | 1 991 | 0.5 992 | 1 993 | 0.5 994 | 1 995 | 0.5 996 | 1 997 | 0.5 998 | 1 999 | 0.5 1000 | 1 1001 | 0.5 1002 | 1 1003 | 0.5 1004 | 1 1005 | 0.5 1006 | 1 1007 | 0.5 1008 | 1 1009 | 0.5 1010 | 1 1011 | 0.5 1012 | 1 1013 | 0.5 1014 | 1 1015 | 0.5 1016 | 1 1017 | 0.5 1018 | 1 1019 | 0.5 1020 | 1 1021 | 0.5 1022 | 1 1023 | 0.5 1024 | 1 1025 | 0.5 1026 | 1 1027 | 0.5 1028 | 1 1029 | 0.5 1030 | 1 1031 | 0.5 1032 | 1 1033 | 0.5 1034 | 1 1035 | 0.5 1036 | 1 1037 | 0.5 1038 | 1 1039 | 0.5 1040 | 1 1041 | 0.5 1042 | 1 1043 | 0.5 1044 | 1 1045 | 0.5 1046 | 1 1047 | 0.5 1048 | 1 1049 | 0.5 1050 | 1 1051 | 0.5 1052 | 1 1053 | 0.5 1054 | 1 1055 | 0.5 1056 | 1 1057 | 0.5 1058 | 1 1059 | 0.5 1060 | 1 1061 | 0.5 1062 | 1 1063 | 0.5 1064 | 1 1065 | 0.5 1066 | 1 1067 | 0.5 1068 | 1 1069 | 0.5 1070 | 1 1071 | 0.5 1072 | 1 1073 | 0.5 1074 | 1 1075 | 0.5 1076 | 1 1077 | 0.5 1078 | 1 1079 | 0.5 1080 | 1 1081 | 0.5 1082 | 1 1083 | 0.5 1084 | 1 1085 | 0.5 1086 | 1 1087 | 0.5 1088 | 1 1089 | 0.5 1090 | 1 1091 | 0.5 1092 | 1 1093 | 0.5 1094 | 1 1095 | 0.5 1096 | 1 1097 | 0.5 1098 | 1 1099 | 0.5 1100 | 1 1101 | 0.5 1102 | 1 1103 | 0.5 1104 | 1 1105 | 0.5 1106 | 1 1107 | 0.5 1108 | 1 1109 | 0.5 1110 | 1 1111 | 0.5 1112 | 1 1113 | 0.5 1114 | 1 1115 | 0.5 1116 | 1 1117 | 0.5 1118 | 1 1119 | 0.5 1120 | 1 1121 | 0.5 1122 | 1 1123 | 0.5 1124 | 1 1125 | 0.5 1126 | 1 1127 | 0.5 1128 | 1 1129 | 0.5 1130 | 1 1131 | 0.5 1132 | 1 1133 | 0.5 1134 | 1 1135 | 0.5 1136 | 1 1137 | 0.5 1138 | 1 1139 | 0.5 1140 | 1 1141 | 0.5 1142 | 1 1143 | 0.5 1144 | 1 1145 | 0.5 1146 | 1 1147 | 0.5 1148 | 1 1149 | 0.5 1150 | 1 1151 | 0.5 1152 | 1 1153 | 0.5 1154 | 1 1155 | 0.5 1156 | 1 1157 | 0.5 1158 | 1 1159 | 0.5 1160 | 1 1161 | 0.5 1162 | 1 1163 | 0.5 1164 | 1 1165 | 0.5 1166 | 1 1167 | 0.5 1168 | 1 1169 | 0.5 1170 | 1 1171 | 0.5 1172 | 1 1173 | 0.5 1174 | 1 1175 | 0.5 1176 | 1 1177 | 0.5 1178 | 1 1179 | 0.5 1180 | 1 1181 | 0.5 1182 | 1 1183 | 0.5 1184 | 1 1185 | 0.5 1186 | 1 1187 | 0.5 1188 | 1 1189 | 0.5 1190 | 1 1191 | 0.5 1192 | 1 1193 | 0.5 1194 | 1 1195 | 0.5 1196 | 1 1197 | 0.5 1198 | 1 1199 | 0.5 1200 | 1 1201 | 0.5 1202 | 1 1203 | 0.5 1204 | 1 1205 | 0.5 1206 | 1 1207 | 0.5 1208 | 1 1209 | 0.5 1210 | 1 1211 | 0.5 1212 | 1 1213 | 0.5 1214 | 1 1215 | 0.5 1216 | 1 1217 | 0.5 1218 | 1 1219 | 0.5 1220 | 1 1221 | 0.5 1222 | 1 1223 | 0.5 1224 | 1 1225 | 0.5 1226 | 1 1227 | 0.5 1228 | 1 1229 | 0.5 1230 | 1 1231 | 0.5 1232 | 1 1233 | 0.5 1234 | 1 1235 | 0.5 1236 | 1 1237 | 0.5 1238 | 1 1239 | 0.5 1240 | 1 1241 | 0.5 1242 | 1 1243 | 0.5 1244 | 1 1245 | 0.5 1246 | 1 1247 | 0.5 1248 | 1 1249 | 0.5 1250 | 1 1251 | 0.5 1252 | 1 1253 | 0.5 1254 | 1 1255 | 0.5 1256 | 1 1257 | 0.5 1258 | 1 1259 | 0.5 1260 | 1 1261 | 0.5 1262 | 1 1263 | 0.5 1264 | 1 1265 | 0.5 1266 | 1 1267 | 0.5 1268 | 1 1269 | 0.5 1270 | 1 1271 | 0.5 1272 | 1 1273 | 0.5 1274 | 1 1275 | 0.5 1276 | 1 1277 | 0.5 1278 | 1 1279 | 0.5 1280 | 1 1281 | 0.5 1282 | 1 1283 | 0.5 1284 | 1 1285 | 0.5 1286 | 1 1287 | 0.5 1288 | 1 1289 | 0.5 1290 | 1 1291 | 0.5 1292 | 1 1293 | 0.5 1294 | 1 1295 | 0.5 1296 | 1 1297 | 0.5 1298 | 1 1299 | 0.5 1300 | 1 1301 | 0.5 1302 | 1 1303 | 0.5 1304 | 1 1305 | 0.5 1306 | 1 1307 | 0.5 1308 | 1 1309 | 0.5 1310 | 1 1311 | 0.5 1312 | 1 1313 | 0.5 1314 | 1 1315 | 0.5 1316 | 1 1317 | 0.5 1318 | 1 1319 | 0.5 1320 | 1 1321 | 0.5 1322 | 1 1323 | 0.5 1324 | 1 1325 | 0.5 1326 | 1 1327 | 0.5 1328 | 1 1329 | 0.5 1330 | 1 1331 | 0.5 1332 | 1 1333 | 0.5 1334 | 1 1335 | 0.5 1336 | 1 1337 | 0.5 1338 | 1 1339 | 0.5 1340 | 1 1341 | -------------------------------------------------------------------------------- /src/nelder.rs: -------------------------------------------------------------------------------- 1 | //! Numerical optimisation using the Nelder-Mead algorithim 2 | //! 3 | //! Translation of the pure Python/Numpy implementation of the Nelder-Mead algorithm. 4 | //! Reference: 5 | 6 | // We use this crate to allow us to add and multiply vectors to keep it in line with python. 7 | // similar to nalgebra but nalgrebra requires way too many subcrates 8 | use vector::MVector; 9 | use log::{info, warn}; 10 | 11 | /// A point on the simplex. 12 | #[derive(Debug, Clone)] 13 | struct Eval { 14 | x: MVector, 15 | score: f64, 16 | } 17 | 18 | // finds the centroids of a list of points 19 | fn centroid(simplex: &[Eval]) -> MVector { 20 | let n = simplex[0].x.len(); 21 | let mut zero = Vec::new(); 22 | 23 | for _ in 0..n { 24 | zero.push(0.0f64); 25 | } 26 | 27 | // let x0 = MVector::from_slice(n, &zero); 28 | let x0 = MVector::from_row_slice(n, &zero); 29 | simplex.iter().fold(x0, |sum, r| sum + r.x.clone()) / simplex.len() as f64 30 | } 31 | 32 | // Keep track of each type of modification. 33 | #[derive(Debug)] 34 | enum Operation { 35 | Reflection, 36 | Expansion, 37 | Contraction, 38 | Reduction, 39 | } 40 | 41 | /// Nelder-Mead specific optimisation parameters. 42 | #[derive(Debug, Clone)] 43 | pub struct Nelder { 44 | /// step increment in each direction from the starting point 45 | pub step: f64, 46 | /// reflection factor (relects the worst point through the centroid) 47 | pub alpha: f64, 48 | /// expansion factor (extends the reflected point) 49 | pub gamma: f64, 50 | /// contraction factor (away from the worst point) 51 | pub rho: f64, 52 | /// shrinking factor (around the best point) 53 | pub sigma: f64, 54 | } 55 | 56 | impl Nelder { 57 | pub fn new(x: &[f64]) -> Nelder { 58 | Nelder { 59 | step: x[0], 60 | alpha: x[1], 61 | gamma: x[2], 62 | rho: x[3], 63 | sigma: x[4], 64 | } 65 | } 66 | pub fn default() -> Nelder { 67 | Nelder { 68 | step: 0.1, 69 | alpha: 1.0, 70 | gamma: 2.0, 71 | rho: 0.2, 72 | sigma: 0.5, 73 | } 74 | } 75 | } 76 | 77 | //// Nelder-Mead non-linear optimisation 78 | /// 79 | /// This routine works best if each of the parameters being optimised are 80 | /// roughly the same size. If this is not the case then they should 81 | /// be normalised to ensure they are. 82 | pub fn nelder_mead( 83 | f: &F, 84 | x: &mut [f64], 85 | params: &Nelder, 86 | converge_tol: f64, 87 | max_iter: usize, 88 | ) -> f64 89 | where 90 | F: Fn(&[f64]) -> f64, 91 | { 92 | check_nelder_limits(params); 93 | 94 | let x_start = MVector::from_row_slice(x.len(), x); 95 | let mut results = Vec::new(); 96 | let mut ops = Vec::new(); 97 | 98 | // results.push(Result {x: x_start.clone(), score: f(x_start.as_ref())}); 99 | results.push(Eval { 100 | x: x_start.clone(), 101 | score: f(x_start.as_slice()), 102 | }); 103 | 104 | // iniitalise the simplex by taking a step in each direction. 105 | for i in 0..x_start.len() { 106 | let mut x_init = x_start.clone(); 107 | // just have to peer inside vec here cause I can't make it work otherwise 108 | x_init.m[i] += params.step; 109 | // results.push(Eval {x: x_init.clone(), score: f(x_init.as_ref())}); 110 | results.push(Eval { 111 | x: x_init.clone(), 112 | score: f(x_init.as_slice()), 113 | }); 114 | } 115 | info!("Nelder: starting result {:?}", results); 116 | 117 | let mut prev_best = results[0].score; 118 | let mut iter = 0; 119 | let n = results.len(); 120 | 121 | loop { 122 | // Check if exceeding the iteration limit. 123 | if iter >= max_iter { 124 | warn!("***Warning: The optimisation has failed to converge within the specified maximum iteration limit {}. 125 | The answer may not be optimum. Try increasing the limit.", max_iter); 126 | break; 127 | } 128 | iter += 1; 129 | 130 | results.sort_by(|a, b| a.score.partial_cmp(&b.score).unwrap()); 131 | info!("Nelder: {} {}. best: {}", iter, n, results[0].score); 132 | 133 | // check for convergence 134 | let change_tol = (results[0].score - results[n - 1].score).abs(); 135 | if change_tol < converge_tol { 136 | info!("Nelder: Success. Converged tol {}", change_tol); 137 | break; 138 | } else { 139 | info!("Nelder: convergence tolerance not reached {}", change_tol); 140 | info!( 141 | "Nelder: best {}, worst {}, prev_best {}", 142 | results[0].score, 143 | results[results.len() - 1].score, 144 | prev_best 145 | ); 146 | } 147 | 148 | prev_best = results[0].score; 149 | 150 | // calculate centro 151 | let x0 = centroid(&results[0..n - 1]); 152 | 153 | // reflection 154 | // if the reflected point is better than the second worst, 155 | // but not better than the best, then replace the worst point 156 | // with the reflected point 157 | let xr = x0.clone() + params.alpha * (x0.clone() - results[n - 1].x.clone()); 158 | // let rscore = f(xr.as_ref()); 159 | let rscore = f(xr.as_slice()); 160 | info!("Nelder: rscore: {}", rscore); 161 | 162 | if rscore >= results[0].score && results[n - 2].score > rscore { 163 | ops.push(Operation::Reflection); 164 | info!("Nelder: Including reflected point"); 165 | 166 | results.pop().unwrap(); 167 | results.push(Eval { 168 | x: xr, 169 | score: rscore, 170 | }); 171 | continue; 172 | } 173 | 174 | // expansion 175 | // If the reflected point is the best point so far 176 | // then compute the expanded point 177 | if rscore < results[0].score { 178 | let xe = x0.clone() + params.gamma * (xr.clone() - x0.clone()); 179 | // let escore = f(xe.as_ref()); 180 | let escore = f(xe.as_slice()); 181 | info!("Nelder: expansion score: {}", escore); 182 | 183 | results.pop().unwrap(); 184 | 185 | if escore < rscore { 186 | ops.push(Operation::Expansion); 187 | results.push(Eval { 188 | x: xe, 189 | score: escore, 190 | }); 191 | continue; 192 | } else { 193 | results.push(Eval { 194 | x: xr, 195 | score: rscore, 196 | }); 197 | continue; 198 | } 199 | } 200 | 201 | // contraction 202 | // If the contracted point is better than the worst point, 203 | // replace the worst point with the contracted point 204 | let xc = x0.clone() + params.rho * (results[n - 1].x.clone() - x0); 205 | // let cscore = f(xc.as_ref()); 206 | let cscore = f(xc.as_slice()); 207 | 208 | if cscore < results[n - 1].score { 209 | info!("contracting: {}", cscore); 210 | 211 | ops.push(Operation::Contraction); 212 | results.pop().unwrap(); 213 | results.push(Eval { 214 | x: xc, 215 | score: cscore, 216 | }); 217 | continue; 218 | } 219 | 220 | // reduction 221 | // For all but the best point, replace the point with 222 | // xi = x1 + sigma(xi - x) 223 | // This is a shrinking of the simplex around the best point 224 | info!("Nelder: reducing"); 225 | 226 | ops.push(Operation::Reduction); 227 | for r in 1..results.len() { 228 | results[r].x = 229 | results[0].x.clone() - params.sigma * (results[r].x.clone() - results[0].x.clone()); 230 | // results[r].score = f(results[r].x.as_ref()); 231 | results[r].score = f(results[r].x.as_slice()); 232 | } 233 | } 234 | 235 | info!("Nelder: Iterations: {}", iter); 236 | let count = count_nelder_ops(&ops); 237 | info!( 238 | "Nelder: Operations: reflection {}, expansion {}, contraction {}, reduction {}", 239 | count.reflection, count.expansion, count.contraction, count.reduction 240 | ); 241 | for (i, &a) in results[0].x.clone().as_slice().to_vec().iter().enumerate() { 242 | x[i] = a; 243 | } 244 | info!("nelder x: {:?}", x); 245 | results[0].score 246 | } 247 | 248 | struct NelderSum { 249 | reflection: usize, 250 | expansion: usize, 251 | contraction: usize, 252 | reduction: usize, 253 | } 254 | 255 | // Count the number of each type of operation in the nelder search. 256 | fn count_nelder_ops(ops: &[Operation]) -> NelderSum { 257 | ops.iter().fold( 258 | NelderSum { 259 | reflection: 0, 260 | expansion: 0, 261 | contraction: 0, 262 | reduction: 0, 263 | }, 264 | |mut count, op| { 265 | match *op { 266 | Operation::Reflection => count.reflection += 1, 267 | Operation::Expansion => count.expansion += 1, 268 | Operation::Contraction => count.contraction += 1, 269 | Operation::Reduction => count.reduction += 1, 270 | }; 271 | 272 | count 273 | }, 274 | ) 275 | } 276 | 277 | /// Check that the Nelder parameters are acceptable. 278 | fn check_nelder_limits(params: &Nelder) { 279 | // recommended parameter limits (wikipedia) 280 | if params.gamma < 0.0 { 281 | warn!("***Warning: Wikipedia recommends using a Nelder-Mead value for gamma > 0.0, using {} .", params.gamma); 282 | } 283 | if { params.rho < 0.0 } | { params.rho > 0.5 } { 284 | warn!( 285 | "***Warning: Wikipedia recommends using a Nelder-Mead value 0.0 < rho < 0.5, using {}.", 286 | params.rho 287 | ); 288 | } 289 | if params.sigma < 0.0 { 290 | warn!( 291 | "***Warning: Wikipedia recommends using a Nelder-Mead value for sigma > 0.0, using {}.", 292 | params.sigma 293 | ); 294 | } 295 | } 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | use vector::MVector; 300 | use log::info; 301 | 302 | use super::{centroid, nelder_mead}; 303 | use super::{Nelder, Eval}; 304 | 305 | #[test] 306 | fn test_centroid() { 307 | let x = vec![ 308 | Eval { 309 | x: MVector::from_row_slice(3, &[0.0, 0.0, 0.0]), 310 | score: 0.0, 311 | }, 312 | Eval { 313 | x: MVector::from_row_slice(3, &[1.0, 2.5, 1.0]), 314 | score: 0.0, 315 | }, 316 | Eval { 317 | x: MVector::from_row_slice(3, &[2.0, 3.5, 2.0]), 318 | score: 0.0, 319 | }, 320 | ]; 321 | 322 | let cent = centroid(&x); 323 | let ans = vec![1.0, 2.0, 1.0]; 324 | 325 | for i in 0..3 { 326 | assert!((ans[i] - cent.m[i]).abs() < std::f64::EPSILON); 327 | } 328 | } 329 | 330 | #[test] 331 | fn test_nelder_sin() { 332 | let f = |x: &[f64]| (x[0].sin() * x[1].cos()) * (1.0 / (x[2].abs() + 1.0)); 333 | 334 | let x = vec![ 335 | Eval { 336 | x: MVector::from_row_slice(3, &[0.0f64, 0.0, 0.0]), 337 | score: 0.0, 338 | }, 339 | Eval { 340 | x: MVector::from_row_slice(3, &[1.0f64, 1.5, 1.0]), 341 | score: 0.0, 342 | }, 343 | Eval { 344 | x: MVector::from_row_slice(3, &[2.0f64, 2.5, 2.0]), 345 | score: 0.0, 346 | }, 347 | ]; 348 | info!("centroid: {:?}", centroid(&x)); 349 | 350 | let mut x = vec![1.0f64, 2.0, 3.0]; 351 | 352 | let f_x = f(&x); 353 | let result = nelder_mead( 354 | &f, 355 | &mut x, 356 | &Nelder { 357 | step: 0.1, 358 | alpha: 1.0, 359 | gamma: 2.0, 360 | rho: 0.5, 361 | sigma: 0.5, 362 | }, 363 | 1e-6, 364 | 100, 365 | ); 366 | 367 | info!("Start result: {}", f_x); 368 | info!("Optimimum result: {:?} at x: {:?}", result, x); 369 | assert!((result + 1.0).abs() < 0.01); 370 | } 371 | 372 | #[test] 373 | fn test_nelder_rosenbrock() { 374 | let mut x = vec![2.0f64, 3.0]; 375 | 376 | let f_x = rosenbrock2d(&x); 377 | let result = nelder_mead( 378 | &rosenbrock2d, 379 | &mut x, 380 | &Nelder { 381 | step: 0.1, 382 | alpha: 1.0, 383 | gamma: 2.0, 384 | rho: 0.5, 385 | sigma: 0.5, 386 | }, 387 | 1e-6, 388 | 100, 389 | ); 390 | 391 | info!("Start result: {}", f_x); 392 | info!("Optimimum result: {:?} at x: {:?}", result, x); 393 | 394 | assert!((result).abs() < 0.01); 395 | assert!(x.iter().fold(0.0, |s, x| s + (x - 1.0).powi(2)) < 0.001); 396 | } 397 | 398 | fn rosenbrock2d(x: &[f64]) -> f64 { 399 | (1.0 - x[0]).powi(2) + 100.0 * (x[1] - x[0].powi(2)).powi(2) 400 | } 401 | 402 | #[test] 403 | fn test_nelder_himmelblau() { 404 | let x_all = vec![[5.0, -5.0], [-5.0, 5.0], [-5.0, -5.0], [0.0, 0.0]]; 405 | let ans_all = vec![ 406 | [3.584_428, -1.848_126], 407 | [-2.805_118, 3.131_312], 408 | [-3.779_310, -3.283_186], 409 | [3.0, 2.0], 410 | ]; 411 | 412 | for (x, ans) in x_all.iter().zip(ans_all) { 413 | let f_x = himmelblau(x); 414 | let mut y = *x; 415 | let result = nelder_mead( 416 | &himmelblau, 417 | &mut y, 418 | &Nelder { 419 | step: 0.1, 420 | alpha: 1.0, 421 | gamma: 2.0, 422 | rho: 0.5, 423 | sigma: 0.5, 424 | }, 425 | 1e-10, 426 | 100, 427 | ); 428 | 429 | info!("Start result: {}", f_x); 430 | info!("Optimimum result: {:?} at x: {:?}", result, y); 431 | 432 | assert!((result).abs() < 1e-5); 433 | let vec_error = y.iter() 434 | .zip(ans.iter()) 435 | .fold(0.0, |s, (x, a)| s + (x - a).powi(2)); 436 | info!( 437 | "vecs error {}, answer {:?} obtained {:?}", 438 | vec_error, ans, y 439 | ); 440 | assert!(vec_error < 1e-5); 441 | } 442 | } 443 | 444 | fn himmelblau(x: &[f64]) -> f64 { 445 | // this has one local maximum at f(-0.270845, -0.923039) = 181.617 446 | // and four identical local minima at 447 | // f(3.0, 2.0) = 0.0 448 | // f(-2.805118, 3.131312) = 0.0 449 | // f(-3.779310, -3.283186) = 0.0 450 | // f(3.584428, -1.848126) = 0.0 451 | 452 | (x[0].powi(2) + x[1] - 11.0).powi(2) + (x[0] + x[1].powi(2) - 7.0).powi(2) 453 | } 454 | 455 | } 456 | -------------------------------------------------------------------------------- /data/sequences/rainflow-seq2.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 0.9 3 | 0.1 4 | 1 5 | 0 6 | 0.9 7 | 0.1 8 | 1 9 | 0 10 | 0.9 11 | 0.1 12 | 1 13 | 0 14 | 0.9 15 | 0.1 16 | 1 17 | 0 18 | 0.9 19 | 0.1 20 | 1 21 | 0 22 | 0.9 23 | 0.1 24 | 1 25 | 0 26 | 0.9 27 | 0.1 28 | 1 29 | 0 30 | 0.9 31 | 0.1 32 | 1 33 | 0 34 | 0.9 35 | 0.1 36 | 1 37 | 0 38 | 0.9 39 | 0.1 40 | 1 41 | 0 42 | 0.9 43 | 0.1 44 | 1 45 | 0 46 | 0.9 47 | 0.1 48 | 1 49 | 0 50 | 0.9 51 | 0.1 52 | 1 53 | 0 54 | 0.9 55 | 0.1 56 | 1 57 | 0 58 | 0.9 59 | 0.1 60 | 1 61 | 0 62 | 0.9 63 | 0.1 64 | 1 65 | 0 66 | 0.9 67 | 0.1 68 | 1 69 | 0 70 | 0.9 71 | 0.1 72 | 1 73 | 0 74 | 0.9 75 | 0.1 76 | 1 77 | 0 78 | 0.9 79 | 0.1 80 | 1 81 | 0 82 | 0.9 83 | 0.1 84 | 1 85 | 0 86 | 0.9 87 | 0.1 88 | 1 89 | 0 90 | 0.9 91 | 0.1 92 | 1 93 | 0 94 | 0.9 95 | 0.1 96 | 1 97 | 0 98 | 0.9 99 | 0.1 100 | 1 101 | 0 102 | 0.9 103 | 0.1 104 | 1 105 | 0 106 | 0.9 107 | 0.1 108 | 1 109 | 0 110 | 0.9 111 | 0.1 112 | 1 113 | 0 114 | 0.9 115 | 0.1 116 | 1 117 | 0 118 | 0.9 119 | 0.1 120 | 1 121 | 0 122 | 0.9 123 | 0.1 124 | 1 125 | 0 126 | 0.9 127 | 0.1 128 | 1 129 | 0 130 | 0.9 131 | 0.1 132 | 1 133 | 0 134 | 0.9 135 | 0.1 136 | 1 137 | 0 138 | 0.9 139 | 0.1 140 | 1 141 | 0 142 | 0.9 143 | 0.1 144 | 1 145 | 0 146 | 0.9 147 | 0.1 148 | 1 149 | 0 150 | 0.9 151 | 0.1 152 | 1 153 | 0 154 | 0.9 155 | 0.1 156 | 1 157 | 0 158 | 0.9 159 | 0.1 160 | 1 161 | 0.25 162 | 0.75 163 | 0.25 164 | 0.75 165 | 0.25 166 | 0.75 167 | 0.25 168 | 0.75 169 | 0.25 170 | 0.75 171 | 0.25 172 | 0.75 173 | 0.25 174 | 0.75 175 | 0.25 176 | 0.75 177 | 0.25 178 | 0.75 179 | 0.25 180 | 0.75 181 | 0.25 182 | 0.75 183 | 0.25 184 | 0.75 185 | 0.25 186 | 0.75 187 | 0.25 188 | 0.75 189 | 0.25 190 | 0.75 191 | 0.25 192 | 0.75 193 | 0.25 194 | 0.75 195 | 0.25 196 | 0.75 197 | 0.25 198 | 0.75 199 | 0.25 200 | 0.75 201 | 0.25 202 | 0.75 203 | 0.25 204 | 0.75 205 | 0.25 206 | 0.75 207 | 0.25 208 | 0.75 209 | 0.25 210 | 0.75 211 | 0.25 212 | 0.75 213 | 0.25 214 | 0.75 215 | 0.25 216 | 0.75 217 | 0.25 218 | 0.75 219 | 0.25 220 | 0.75 221 | 0.25 222 | 0.75 223 | 0.25 224 | 0.75 225 | 0.25 226 | 0.75 227 | 0.25 228 | 0.75 229 | 0.25 230 | 0.75 231 | 0.25 232 | 0.75 233 | 0.25 234 | 0.75 235 | 0.25 236 | 0.75 237 | 0.25 238 | 0.75 239 | 0.25 240 | 0.75 241 | 0.25 242 | 0.75 243 | 0.25 244 | 0.75 245 | 0.25 246 | 0.75 247 | 0.25 248 | 0.75 249 | 0.25 250 | 0.75 251 | 0.25 252 | 0.75 253 | 0.25 254 | 0.75 255 | 0.25 256 | 0.75 257 | 0.25 258 | 0.75 259 | 0.25 260 | 0.75 261 | 0.1 262 | 0.9 263 | 0.1 264 | 0.9 265 | 0.1 266 | 0.9 267 | 0.1 268 | 0.9 269 | 0.1 270 | 0.9 271 | 0.1 272 | 0.9 273 | 0.1 274 | 0.9 275 | 0.1 276 | 0.9 277 | 0.1 278 | 0.9 279 | 0.1 280 | 0.9 281 | 0.1 282 | 0.9 283 | 0.1 284 | 0.9 285 | 0.1 286 | 0.9 287 | 0.1 288 | 0.9 289 | 0.1 290 | 0.9 291 | 0.1 292 | 0.9 293 | 0.1 294 | 0.9 295 | 0.1 296 | 0.9 297 | 0.1 298 | 0.9 299 | 0.1 300 | 0.9 301 | 0.1 302 | 0.9 303 | 0.1 304 | 0.9 305 | 0.1 306 | 0.9 307 | 0.1 308 | 0.9 309 | 0.1 310 | 0.9 311 | 0.1 312 | 0.9 313 | 0.1 314 | 0.9 315 | 0.1 316 | 0.9 317 | 0.1 318 | 0.9 319 | 0.1 320 | 0.9 321 | 0.1 322 | 0.9 323 | 0.1 324 | 0.9 325 | 0.1 326 | 0.9 327 | 0.1 328 | 0.9 329 | 0.1 330 | 0.9 331 | 0.1 332 | 0.9 333 | 0.1 334 | 0.9 335 | 0.1 336 | 0.9 337 | 0.1 338 | 0.9 339 | 0.1 340 | 0.9 341 | 0 342 | 1 343 | 0 344 | 1 345 | 0 346 | 1 347 | 0 348 | 1 349 | 0 350 | 1 351 | 0 352 | 1 353 | 0 354 | 1 355 | 0 356 | 1 357 | 0 358 | 1 359 | 0 360 | 1 361 | 0 362 | 1 363 | 0 364 | 1 365 | 0 366 | 1 367 | 0 368 | 1 369 | 0 370 | 1 371 | 0 372 | 1 373 | 0 374 | 1 375 | 0 376 | 1 377 | 0 378 | 1 379 | 0 380 | 1 381 | 0 382 | 1 383 | 0 384 | 1 385 | 0 386 | 1 387 | 0 388 | 1 389 | 0 390 | 1 391 | 0 392 | 1 393 | 0 394 | 1 395 | 0 396 | 1 397 | 0 398 | 1 399 | 0 400 | 1 401 | 0 402 | 1 403 | 0 404 | 1 405 | 0 406 | 1 407 | 0 408 | 1 409 | 0 410 | 1 411 | 0 412 | 1 413 | 0 414 | 1 415 | 0 416 | 1 417 | 0 418 | 1 419 | 0 420 | 1 421 | 0.25 422 | 0.75 423 | 0.25 424 | 0.75 425 | 0.25 426 | 0.75 427 | 0.25 428 | 0.75 429 | 0.25 430 | 0.75 431 | 0.25 432 | 0.75 433 | 0.25 434 | 0.75 435 | 0.25 436 | 0.75 437 | 0.25 438 | 0.75 439 | 0.25 440 | 0.75 441 | 0.25 442 | 0.75 443 | 0.25 444 | 0.75 445 | 0.25 446 | 0.75 447 | 0.25 448 | 0.75 449 | 0.25 450 | 0.75 451 | 0.25 452 | 0.75 453 | 0.25 454 | 0.75 455 | 0.25 456 | 0.75 457 | 0.25 458 | 0.75 459 | 0.25 460 | 0.75 461 | 0.25 462 | 0.75 463 | 0.25 464 | 0.75 465 | 0.25 466 | 0.75 467 | 0.25 468 | 0.75 469 | 0.25 470 | 0.75 471 | 0.25 472 | 0.75 473 | 0.25 474 | 0.75 475 | 0.25 476 | 0.75 477 | 0.25 478 | 0.75 479 | 0.25 480 | 0.75 481 | 0.25 482 | 0.75 483 | 0.25 484 | 0.75 485 | 0.25 486 | 0.75 487 | 0.25 488 | 0.75 489 | 0.25 490 | 0.75 491 | 0.25 492 | 0.75 493 | 0.25 494 | 0.75 495 | 0.25 496 | 0.75 497 | 0.25 498 | 0.75 499 | 0.25 500 | 0.75 501 | 0.25 502 | 0.75 503 | 0.25 504 | 0.75 505 | 0.25 506 | 0.75 507 | 0.25 508 | 0.75 509 | 0.25 510 | 0.75 511 | 0.25 512 | 0.75 513 | 0.25 514 | 0.75 515 | 0.25 516 | 0.75 517 | 0.25 518 | 0.75 519 | 0.25 520 | 0.75 521 | 0 522 | 0.9 523 | 0 524 | 0.9 525 | 0 526 | 0.9 527 | 0 528 | 0.9 529 | 0 530 | 0.9 531 | 0 532 | 0.9 533 | 0 534 | 0.9 535 | 0 536 | 0.9 537 | 0 538 | 0.9 539 | 0 540 | 0.9 541 | 0 542 | 0.9 543 | 0 544 | 0.9 545 | 0 546 | 0.9 547 | 0 548 | 0.9 549 | 0 550 | 0.9 551 | 0 552 | 0.9 553 | 0 554 | 0.9 555 | 0 556 | 0.9 557 | 0 558 | 0.9 559 | 0 560 | 0.9 561 | 0 562 | 0.9 563 | 0 564 | 0.9 565 | 0 566 | 0.9 567 | 0 568 | 0.9 569 | 0 570 | 0.9 571 | 0 572 | 0.9 573 | 0 574 | 0.9 575 | 0 576 | 0.9 577 | 0 578 | 0.9 579 | 0 580 | 0.9 581 | 0 582 | 0.9 583 | 0 584 | 0.9 585 | 0 586 | 0.9 587 | 0 588 | 0.9 589 | 0 590 | 0.9 591 | 0 592 | 0.9 593 | 0 594 | 0.9 595 | 0 596 | 0.9 597 | 0 598 | 0.9 599 | 0 600 | 0.9 601 | 0.1 602 | 1 603 | 0.1 604 | 1 605 | 0.1 606 | 1 607 | 0.1 608 | 1 609 | 0.1 610 | 1 611 | 0.1 612 | 1 613 | 0.1 614 | 1 615 | 0.1 616 | 1 617 | 0.1 618 | 1 619 | 0.1 620 | 1 621 | 0.1 622 | 1 623 | 0.1 624 | 1 625 | 0.1 626 | 1 627 | 0.1 628 | 1 629 | 0.1 630 | 1 631 | 0.1 632 | 1 633 | 0.1 634 | 1 635 | 0.1 636 | 1 637 | 0.1 638 | 1 639 | 0.1 640 | 1 641 | 0.1 642 | 1 643 | 0.1 644 | 1 645 | 0.1 646 | 1 647 | 0.1 648 | 1 649 | 0.1 650 | 1 651 | 0.1 652 | 1 653 | 0.1 654 | 1 655 | 0.1 656 | 1 657 | 0.1 658 | 1 659 | 0.1 660 | 1 661 | 0.1 662 | 1 663 | 0.1 664 | 1 665 | 0.1 666 | 1 667 | 0.1 668 | 1 669 | 0.1 670 | 1 671 | 0.1 672 | 1 673 | 0.1 674 | 1 675 | 0.1 676 | 1 677 | 0.1 678 | 1 679 | 0.1 680 | 1 681 | 0.25 682 | 0.75 683 | 0.25 684 | 0.75 685 | 0.25 686 | 0.75 687 | 0.25 688 | 0.75 689 | 0.25 690 | 0.75 691 | 0.25 692 | 0.75 693 | 0.25 694 | 0.75 695 | 0.25 696 | 0.75 697 | 0.25 698 | 0.75 699 | 0.25 700 | 0.75 701 | 0.25 702 | 0.75 703 | 0.25 704 | 0.75 705 | 0.25 706 | 0.75 707 | 0.25 708 | 0.75 709 | 0.25 710 | 0.75 711 | 0.25 712 | 0.75 713 | 0.25 714 | 0.75 715 | 0.25 716 | 0.75 717 | 0.25 718 | 0.75 719 | 0.25 720 | 0.75 721 | 0.25 722 | 0.75 723 | 0.25 724 | 0.75 725 | 0.25 726 | 0.75 727 | 0.25 728 | 0.75 729 | 0.25 730 | 0.75 731 | 0.25 732 | 0.75 733 | 0.25 734 | 0.75 735 | 0.25 736 | 0.75 737 | 0.25 738 | 0.75 739 | 0.25 740 | 0.75 741 | 0.25 742 | 0.75 743 | 0.25 744 | 0.75 745 | 0.25 746 | 0.75 747 | 0.25 748 | 0.75 749 | 0.25 750 | 0.75 751 | 0.25 752 | 0.75 753 | 0.25 754 | 0.75 755 | 0.25 756 | 0.75 757 | 0.25 758 | 0.75 759 | 0.25 760 | 0.75 761 | 0.25 762 | 0.75 763 | 0.25 764 | 0.75 765 | 0.25 766 | 0.75 767 | 0.25 768 | 0.75 769 | 0.25 770 | 0.75 771 | 0.25 772 | 0.75 773 | 0.25 774 | 0.75 775 | 0.25 776 | 0.75 777 | 0.25 778 | 0.75 779 | 0.25 780 | 0.75 781 | 0 782 | 1 783 | 0.1 784 | 0.9 785 | 0 786 | 1 787 | 0.1 788 | 0.9 789 | 0 790 | 1 791 | 0.1 792 | 0.9 793 | 0 794 | 1 795 | 0.1 796 | 0.9 797 | 0 798 | 1 799 | 0.1 800 | 0.9 801 | 0 802 | 1 803 | 0.1 804 | 0.9 805 | 0 806 | 1 807 | 0.1 808 | 0.9 809 | 0 810 | 1 811 | 0.1 812 | 0.9 813 | 0 814 | 1 815 | 0.1 816 | 0.9 817 | 0 818 | 1 819 | 0.1 820 | 0.9 821 | 0 822 | 1 823 | 0.1 824 | 0.9 825 | 0 826 | 1 827 | 0.1 828 | 0.9 829 | 0 830 | 1 831 | 0.1 832 | 0.9 833 | 0 834 | 1 835 | 0.1 836 | 0.9 837 | 0 838 | 1 839 | 0.1 840 | 0.9 841 | 0 842 | 1 843 | 0.1 844 | 0.9 845 | 0 846 | 1 847 | 0.1 848 | 0.9 849 | 0 850 | 1 851 | 0.1 852 | 0.9 853 | 0 854 | 1 855 | 0.1 856 | 0.9 857 | 0 858 | 1 859 | 0.1 860 | 0.9 861 | 0 862 | 1 863 | 0.1 864 | 0.9 865 | 0 866 | 1 867 | 0.1 868 | 0.9 869 | 0 870 | 1 871 | 0.1 872 | 0.9 873 | 0 874 | 1 875 | 0.1 876 | 0.9 877 | 0 878 | 1 879 | 0.1 880 | 0.9 881 | 0 882 | 1 883 | 0.1 884 | 0.9 885 | 0 886 | 1 887 | 0.1 888 | 0.9 889 | 0 890 | 1 891 | 0.1 892 | 0.9 893 | 0 894 | 1 895 | 0.1 896 | 0.9 897 | 0 898 | 1 899 | 0.1 900 | 0.9 901 | 0 902 | 1 903 | 0.1 904 | 0.9 905 | 0 906 | 1 907 | 0.1 908 | 0.9 909 | 0 910 | 1 911 | 0.1 912 | 0.9 913 | 0 914 | 1 915 | 0.1 916 | 0.9 917 | 0 918 | 1 919 | 0.1 920 | 0.9 921 | 0 922 | 1 923 | 0.1 924 | 0.9 925 | 0 926 | 1 927 | 0.1 928 | 0.9 929 | 0 930 | 1 931 | 0.1 932 | 0.9 933 | 0 934 | 1 935 | 0.1 936 | 0.9 937 | 0 938 | 1 939 | 0.1 940 | 0.9 941 | 0.25 942 | 0.75 943 | 0.25 944 | 0.75 945 | 0.25 946 | 0.75 947 | 0.25 948 | 0.75 949 | 0.25 950 | 0.75 951 | 0.25 952 | 0.75 953 | 0.25 954 | 0.75 955 | 0.25 956 | 0.75 957 | 0.25 958 | 0.75 959 | 0.25 960 | 0.75 961 | 0.25 962 | 0.75 963 | 0.25 964 | 0.75 965 | 0.25 966 | 0.75 967 | 0.25 968 | 0.75 969 | 0.25 970 | 0.75 971 | 0.25 972 | 0.75 973 | 0.25 974 | 0.75 975 | 0.25 976 | 0.75 977 | 0.25 978 | 0.75 979 | 0.25 980 | 0.75 981 | 0.25 982 | 0.75 983 | 0.25 984 | 0.75 985 | 0.25 986 | 0.75 987 | 0.25 988 | 0.75 989 | 0.25 990 | 0.75 991 | 0.25 992 | 0.75 993 | 0.25 994 | 0.75 995 | 0.25 996 | 0.75 997 | 0.25 998 | 0.75 999 | 0.25 1000 | 0.75 1001 | 0.25 1002 | 0.75 1003 | 0.25 1004 | 0.75 1005 | 0.25 1006 | 0.75 1007 | 0.25 1008 | 0.75 1009 | 0.25 1010 | 0.75 1011 | 0.25 1012 | 0.75 1013 | 0.25 1014 | 0.75 1015 | 0.25 1016 | 0.75 1017 | 0.25 1018 | 0.75 1019 | 0.25 1020 | 0.75 1021 | 0.25 1022 | 0.75 1023 | 0.25 1024 | 0.75 1025 | 0.25 1026 | 0.75 1027 | 0.25 1028 | 0.75 1029 | 0.25 1030 | 0.75 1031 | 0.25 1032 | 0.75 1033 | 0.25 1034 | 0.75 1035 | 0.25 1036 | 0.75 1037 | 0.25 1038 | 0.75 1039 | 0.25 1040 | 0.75 1041 | 0.25 1042 | 0.75 1043 | 0.25 1044 | 0.75 1045 | 0.25 1046 | 0.75 1047 | 0.25 1048 | 0.75 1049 | 0.25 1050 | 0.75 1051 | 0.25 1052 | 0.75 1053 | 0.25 1054 | 0.75 1055 | 0.25 1056 | 0.75 1057 | 0.25 1058 | 0.75 1059 | 0.25 1060 | 0.75 1061 | 0.25 1062 | 0.75 1063 | 0.25 1064 | 0.75 1065 | 0.25 1066 | 0.75 1067 | 0.25 1068 | 0.75 1069 | 0.25 1070 | 0.75 1071 | 0.25 1072 | 0.75 1073 | 0.25 1074 | 0.75 1075 | 0.25 1076 | 0.75 1077 | 0.25 1078 | 0.75 1079 | 0.25 1080 | 0.75 1081 | 0.25 1082 | 0.75 1083 | 0.25 1084 | 0.75 1085 | 0.25 1086 | 0.75 1087 | 0.25 1088 | 0.75 1089 | 0.25 1090 | 0.75 1091 | 0.25 1092 | 0.75 1093 | 0.25 1094 | 0.75 1095 | 0.25 1096 | 0.75 1097 | 0.25 1098 | 0.75 1099 | 0.25 1100 | 0.75 1101 | 0.25 1102 | 0.75 1103 | 0.25 1104 | 0.75 1105 | 0.25 1106 | 0.75 1107 | 0.25 1108 | 0.75 1109 | 0.25 1110 | 0.75 1111 | 0.25 1112 | 0.75 1113 | 0.25 1114 | 0.75 1115 | 0.25 1116 | 0.75 1117 | 0.25 1118 | 0.75 1119 | 0.25 1120 | 0.75 1121 | 0.25 1122 | 0.75 1123 | 0.25 1124 | 0.75 1125 | 0.25 1126 | 0.75 1127 | 0.25 1128 | 0.75 1129 | 0.25 1130 | 0.75 1131 | 0.25 1132 | 0.75 1133 | 0.25 1134 | 0.75 1135 | 0.25 1136 | 0.75 1137 | 0.25 1138 | 0.75 1139 | 0.25 1140 | 0.75 1141 | 0.25 1142 | 0.75 1143 | 0.25 1144 | 0.75 1145 | 0.25 1146 | 0.75 1147 | 0.25 1148 | 0.75 1149 | 0.25 1150 | 0.75 1151 | 0.25 1152 | 0.75 1153 | 0.25 1154 | 0.75 1155 | 0.25 1156 | 0.75 1157 | 0.25 1158 | 0.75 1159 | 0.25 1160 | 0.75 1161 | 0.25 1162 | 0.75 1163 | 0.25 1164 | 0.75 1165 | 0.25 1166 | 0.75 1167 | 0.25 1168 | 0.75 1169 | 0.25 1170 | 0.75 1171 | 0.25 1172 | 0.75 1173 | 0.25 1174 | 0.75 1175 | 0.25 1176 | 0.75 1177 | 0.25 1178 | 0.75 1179 | 0.25 1180 | 0.75 1181 | 0.25 1182 | 0.75 1183 | 0.25 1184 | 0.75 1185 | 0.25 1186 | 0.75 1187 | 0.25 1188 | 0.75 1189 | 0.25 1190 | 0.75 1191 | 0.25 1192 | 0.75 1193 | 0.25 1194 | 0.75 1195 | 0.25 1196 | 0.75 1197 | 0.25 1198 | 0.75 1199 | 0.25 1200 | 0.75 1201 | 0.25 1202 | 0.75 1203 | 0.25 1204 | 0.75 1205 | 0.25 1206 | 0.75 1207 | 0.25 1208 | 0.75 1209 | 0.25 1210 | 0.75 1211 | 0.25 1212 | 0.75 1213 | 0.25 1214 | 0.75 1215 | 0.25 1216 | 0.75 1217 | 0.25 1218 | 0.75 1219 | 0.25 1220 | 0.75 1221 | 0.25 1222 | 0.75 1223 | 0.25 1224 | 0.75 1225 | 0.25 1226 | 0.75 1227 | 0.25 1228 | 0.75 1229 | 0.25 1230 | 0.75 1231 | 0.25 1232 | 0.75 1233 | 0.25 1234 | 0.75 1235 | 0.25 1236 | 0.75 1237 | 0.25 1238 | 0.75 1239 | 0.25 1240 | 0.75 1241 | 0.25 1242 | 0.75 1243 | 0.25 1244 | 0.75 1245 | 0.25 1246 | 0.75 1247 | 0.25 1248 | 0.75 1249 | 0.25 1250 | 0.75 1251 | 0.25 1252 | 0.75 1253 | 0.25 1254 | 0.75 1255 | 0.25 1256 | 0.75 1257 | 0.25 1258 | 0.75 1259 | 0.25 1260 | 0.75 1261 | 0.25 1262 | 0.75 1263 | 0.25 1264 | 0.75 1265 | 0.25 1266 | 0.75 1267 | 0.25 1268 | 0.75 1269 | 0.25 1270 | 0.75 1271 | 0.25 1272 | 0.75 1273 | 0.25 1274 | 0.75 1275 | 0.25 1276 | 0.75 1277 | 0.25 1278 | 0.75 1279 | 0.25 1280 | 0.75 1281 | 0.25 1282 | 0.75 1283 | 0.25 1284 | 0.75 1285 | 0.25 1286 | 0.75 1287 | 0.25 1288 | 0.75 1289 | 0.25 1290 | 0.75 1291 | 0.25 1292 | 0.75 1293 | 0.25 1294 | 0.75 1295 | 0.25 1296 | 0.75 1297 | 0.25 1298 | 0.75 1299 | 0.25 1300 | 0.75 1301 | 0.25 1302 | 0.75 1303 | 0.25 1304 | 0.75 1305 | 0.25 1306 | 0.75 1307 | 0.25 1308 | 0.75 1309 | 0.25 1310 | 0.75 1311 | 0.25 1312 | 0.75 1313 | 0.25 1314 | 0.75 1315 | 0.25 1316 | 0.75 1317 | 0.25 1318 | 0.75 1319 | 0.25 1320 | 0.75 1321 | 0.25 1322 | 0.75 1323 | 0.25 1324 | 0.75 1325 | 0.25 1326 | 0.75 1327 | 0.25 1328 | 0.75 1329 | 0.25 1330 | 0.75 1331 | 0.25 1332 | 0.75 1333 | 0.25 1334 | 0.75 1335 | 0.25 1336 | 0.75 1337 | 0.25 1338 | 0.75 1339 | 0.25 1340 | 0.75 1341 | -------------------------------------------------------------------------------- /src/grow.rs: -------------------------------------------------------------------------------- 1 | //! Grow a fatigue crack from initial crack size until failure. 2 | #![allow(clippy::useless_let_if_seq)] 3 | 4 | use std::f64::consts::FRAC_PI_2; 5 | use COMMENT; 6 | use std::process; 7 | use std::f64::consts::PI; 8 | use std::collections::BTreeSet; 9 | use cycle::Cycle; 10 | use plastic; 11 | use dadn; 12 | use material; 13 | use tag; 14 | use beta; 15 | 16 | /// Data collected for each crack growth prediction cycle. 17 | #[derive(Debug, Clone)] 18 | pub struct History { 19 | /// Floating point number for block where fractional parts gives fraction of cycle in the block 20 | pub block: f64, 21 | /// Applied scaling stress for this cycle. 22 | pub stress: f64, 23 | /// Cycle information. 24 | pub cycle: Cycle, 25 | /// Stress intensity around the crack 26 | pub k: Vec, 27 | /// stress intensity range 28 | pub dk: Vec, 29 | /// beta values around the crack front 30 | pub beta: Vec, 31 | /// growth increment around the crack front 32 | pub da: Vec, 33 | pub crack: CrackState, 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct CrackState { 38 | /// Crack length at each point around the crack front 39 | pub a: Vec, 40 | /// distance of monotonic zone from crack tip 41 | pub mono_zone_extent: plastic::ZoneWidth, 42 | /// distance of cyclic zone from crack tip 43 | pub cyclic_zone_extent: plastic::ZoneWidth, 44 | } 45 | 46 | impl CrackState { 47 | pub fn new(a: Vec) -> CrackState { 48 | CrackState { 49 | a, 50 | mono_zone_extent: plastic::ZoneWidth { 51 | plane_stress: 0.0, 52 | plane_strain: 0.0, 53 | }, 54 | cyclic_zone_extent: plastic::ZoneWidth { 55 | plane_stress: 0.0, 56 | plane_strain: 0.0, 57 | }, 58 | } 59 | } 60 | } 61 | 62 | #[derive(Debug, Clone)] 63 | /// describes the geometry and material containing the crack 64 | pub struct Component { 65 | pub forward: f64, 66 | pub sideways: f64, 67 | pub radius: f64, 68 | pub material: material::Properties, 69 | } 70 | 71 | // All the data necessary to run a fatigue calculation. 72 | pub struct FatigueTest { 73 | pub history: History, 74 | pub component: Component, 75 | pub scale: f64, 76 | pub cycles: Vec>, 77 | pub a_limit: Vec, 78 | pub block_limit: f64, 79 | pub next_cycle: usize, 80 | pub dadn: Box, 81 | pub beta: Box, 82 | pub output_vars: Vec, 83 | } 84 | 85 | // Create an interator that can step through a fatigue test struct to 86 | // generate the history for each cycle. needs a 'history' and a set 87 | // of 'options'. 88 | impl Iterator for FatigueTest { 89 | type Item = History; 90 | 91 | fn next(&mut self) -> Option { 92 | // increment the next cycle 93 | self.history.block = 94 | self.history.block.floor() + (self.next_cycle as f64 + 1.0) / self.cycles.len() as f64; 95 | self.next_cycle = (self.next_cycle + 1) % self.cycles.len(); 96 | let tagged_cycle = self.cycles[self.next_cycle]; 97 | 98 | let history = self.history.clone(); 99 | // grow the crack for one cycle 100 | self.history = history.grow_crack( 101 | &tagged_cycle, 102 | self.scale, 103 | &self.dadn, 104 | &mut self.beta, 105 | &self.component, 106 | ); 107 | 108 | // Check for terminating conditions. 109 | let reached_limit = reached_limit( 110 | self.history.block, 111 | self.block_limit, 112 | &self.history.crack.a, 113 | &self.a_limit, 114 | ); 115 | let component_failed = component_failed( 116 | &self.history.crack.a, 117 | self.history.stress * self.history.cycle.max.value, 118 | self.history.k[0], 119 | &self.component, 120 | &self.beta, 121 | ); 122 | 123 | if reached_limit.failure || component_failed.failure { 124 | // Print the final line at failure. 125 | display_history_line(&self.history, &self.output_vars, &self.component); 126 | let messages = reached_limit.messages + &component_failed.messages; 127 | println!("{}", messages); 128 | None 129 | } else { 130 | Some(self.history.clone()) 131 | } 132 | } 133 | } 134 | 135 | /// Performs a crack growth calculation for a single cycle. 136 | impl History { 137 | pub fn grow_crack( 138 | self, 139 | cycle: &Cycle, 140 | scale: f64, 141 | eqn: &Box, 142 | beta: &mut Box, 143 | component: &Component, 144 | ) -> History { 145 | 146 | // destructure the given cycle 147 | let Cycle { 148 | max: tag::Tag { 149 | value: smax, 150 | .. 151 | }, 152 | min: tag::Tag { 153 | value: smin, 154 | .. 155 | }, 156 | } = *cycle; 157 | 158 | let r = smin / smax; 159 | if smax < smin { 160 | println!( 161 | "Program Error: smax {} is less than smin {}. This should never happen.", 162 | smax, smin 163 | ); 164 | process::exit(1); 165 | } 166 | 167 | let c = self.crack.a[self.crack.a.len() - 1]; 168 | let a_on_c = self.crack.a[0] / c; 169 | let a_on_d = self.crack.a[0] / component.forward; 170 | let c_on_b = c / component.sideways; 171 | let a_on_r = self.crack.a[0] / component.radius; 172 | let phis = vec![0.0, FRAC_PI_2]; 173 | 174 | let betas = beta.beta(a_on_d, a_on_c, c_on_b, a_on_r, &phis); 175 | 176 | // values around the crack front 177 | let mut da_all: Vec = Vec::new(); 178 | let mut k_all: Vec = Vec::new(); 179 | let mut dk_all: Vec = Vec::new(); 180 | 181 | // println!("Scale is {}", scale); 182 | // calculate the growth for each 'a' around the crack 183 | // front. We assume the beta is calculated at each point 184 | // around the crack front but we know that for some beta 185 | // functions this is not possible. 186 | let mut a_all = Vec::with_capacity(self.crack.a.len()); 187 | for (beta, a) in betas.iter().zip(self.crack.a.iter()) { 188 | let k_on_stress = scale * beta * (PI * *a).sqrt(); 189 | let kmin = smin * k_on_stress; 190 | let kmax = smax * k_on_stress; 191 | let dk = kmax - kmin; 192 | 193 | k_all.push(kmax); 194 | dk_all.push(dk); 195 | 196 | // grow the crack an increment 197 | let da = eqn.dadn(kmin, kmax, dadn::CrackState{ a: *a }); 198 | if da.is_nan() { 199 | println!( 200 | "Error: the dadn calculation has returned an NAN beta {} cycle_dk {} r {}", 201 | beta, dk, r 202 | ); 203 | process::exit(1); 204 | } 205 | a_all.push(a + da); 206 | da_all.push(da); 207 | } 208 | 209 | // Calculate the size of the plastic zone based on the largest k and dk around the crack front 210 | let kmax = k_all.iter().fold(k_all[0], |f, x| x.max(f)); 211 | let dkmax = dk_all.iter().fold(dk_all[0], |f, x| x.max(f)); 212 | 213 | let mono_zone_extent = plastic::zone_size(kmax, component.material.yield_stress); 214 | // Double the yield stress to get the cyclic stress yield stress. 215 | let cyclic_zone_extent = plastic::zone_size(dkmax, 2.0 * component.material.yield_stress); 216 | 217 | History { 218 | block: self.block, // part_block, 219 | da: da_all, 220 | k: k_all, 221 | dk: dk_all, 222 | cycle: *cycle, 223 | stress: scale, 224 | beta: betas, 225 | crack: CrackState { 226 | a: a_all, 227 | mono_zone_extent, 228 | cyclic_zone_extent, 229 | }, 230 | } 231 | } 232 | } 233 | 234 | pub fn display_history_header(output: &[String]) { 235 | // write out the headers 236 | if !output.is_empty() { 237 | for out in output.iter() { 238 | print!("{:>12} ", out); 239 | } 240 | println!(); 241 | } 242 | } 243 | 244 | /// Test to see if a history line should be output. 245 | pub fn output_cycle_history( 246 | his: &History, 247 | every: i32, 248 | output_lines: &BTreeSet, 249 | cycle_no: usize, 250 | ) -> bool { 251 | // if every is positive write out every nth block, otherwise if 252 | // every is negative write out every nth cycle 253 | let frequency = (every > 0 && his.block as i32 % every == 0) 254 | || (every < 0 && cycle_no % -every as usize == 0); 255 | // println!("freq {} every {}, lines {:?}, block {}, cycle_no {}, max {} min {}", frequency, every, output_lines, his.block, cycle_no, his.cycle.max.index,his.cycle.min.index); 256 | 257 | // output only if the cycle constains the specific sequence line 258 | if !output_lines.is_empty() && every > 0 { 259 | frequency 260 | && (output_lines.contains(&his.cycle.max.index) 261 | || output_lines.contains(&his.cycle.min.index)) 262 | } else { 263 | frequency 264 | } 265 | } 266 | 267 | // print a line of the history data 268 | pub fn display_history_line(his: &History, output: &[String], component: &Component) { 269 | let a = his.crack.a[0]; 270 | let c = his.crack.a[his.crack.a.len() - 1]; 271 | 272 | for out in output { 273 | match out.trim() { 274 | "line" => print!("{:12} ", his.cycle.max.index), 275 | "block" => print!("{:12.4} ", his.block), 276 | "a/c" => print!("{:12.4} ", a / c), 277 | "a/d" => print!("{:12.4} ", a / component.forward), 278 | "c/b" => print!("{:12.4} ", c / component.sideways), 279 | "k" => print!("{:12.4} ", his.k[0]), 280 | "dk" => print!("{:12.4} ", his.dk[0]), 281 | "r" => print!("{:12.4} ", his.cycle.min.value / his.cycle.max.value), 282 | "beta_a" => print!("{:12.4e} ", his.beta[0]), 283 | "beta_c" => print!("{:12.4e} ", his.beta[his.beta.len() - 1]), 284 | "a" => print!("{:12.6e} ", a), 285 | "c" => print!("{:12.6e} ", c), 286 | "da" => print!("{:12.4e} ", his.da[0]), 287 | "dc" => print!("{:12.4e} ", his.da[his.da.len() - 1]), 288 | "mono" => print!("{:12.4e} ", his.crack.mono_zone_extent.plane_strain), 289 | "cyclic" => print!("{:12.4e} ", his.crack.cyclic_zone_extent.plane_strain), 290 | "a/mono" => print!("{:12.2} ", a / his.crack.mono_zone_extent.plane_strain), 291 | "a/cyclic" => print!("{:12.4} ", a / his.crack.cyclic_zone_extent.plane_strain), 292 | "mono/da" => print!( 293 | "{:12.4}", 294 | his.crack.mono_zone_extent.plane_strain / his.da[0] 295 | ), 296 | "cyclic/da" => print!( 297 | "{:12.4}", 298 | his.crack.cyclic_zone_extent.plane_strain / his.da[0] 299 | ), 300 | "peak" => print!("{:12.6} ", his.stress * his.cycle.max.value), 301 | "valley" => print!("{:12.6} ", his.stress * his.cycle.min.value), 302 | ref opt => { 303 | println!( 304 | "Error: Unknown output option (use the --list option for a complete list): {}", 305 | opt 306 | ); 307 | process::exit(1); 308 | } 309 | }; 310 | } 311 | println!(); 312 | } 313 | 314 | /// This structure provides a way of remembering the failure messages 315 | /// so that they can be written out at the end. 316 | pub struct FailureResult { 317 | /// Type of failure. 318 | pub failure: bool, 319 | /// Failure Message. 320 | pub messages: String, 321 | } 322 | 323 | /// check if we have reached a pre-defined crack growth limit. 324 | pub fn reached_limit( 325 | part_block: f64, 326 | block_limit: f64, 327 | a: &[f64], 328 | a_limit: &[f64], 329 | ) -> FailureResult { 330 | let mut message = "".to_string(); 331 | let mut terminate = false; 332 | 333 | if part_block >= block_limit { 334 | message += &format!( 335 | "{}Run stopped because hit block limit {}\n", 336 | COMMENT, block_limit 337 | ); 338 | terminate = true; 339 | } 340 | 341 | if !a_limit.is_empty() && a.iter().zip(a_limit).any(|(a, e)| a >= e) { 342 | message += &format!( 343 | "{}Failure Event: a{:?} >= a_limit{:?}\n", 344 | COMMENT, a, a_limit 345 | ); 346 | terminate = true; 347 | } 348 | 349 | FailureResult { 350 | failure: terminate, 351 | messages: message, 352 | } 353 | } 354 | 355 | /// Check whether the crack has exceeded any failure criteria for the component. 356 | pub fn component_failed( 357 | a: &[f64], 358 | smax: f64, 359 | kmax: f64, 360 | component: &Component, 361 | _beta: &Box, 362 | ) -> FailureResult { 363 | 364 | let mut terminate = false; 365 | let mut message = "".to_string(); 366 | 367 | // clippy says to do it this way, but its a bit inconsistent with the multiple failure checks 368 | // Check whether we have satisfied any termination criteria. 369 | // let mut terminate = if component.forward > 0.0 && a[0] > component.forward { 370 | // message += &format!( 371 | // "{}Failure Event: a[{}] > depth[{}]\n", 372 | // COMMENT, a[0], component.forward 373 | // ); 374 | // true } else { false }; 375 | 376 | if component.forward > 0.0 && a[0] > component.forward { 377 | message += &format!( 378 | "{}Failure Event: a[{}] > depth[{}]\n", 379 | COMMENT, a[0], component.forward 380 | ); 381 | terminate = true; 382 | } 383 | 384 | if component.material.k1c > 0.0 && kmax > component.material.k1c { 385 | message += &format!( 386 | "{}Failure Event: k[{}] > k1c[{}]\n", 387 | COMMENT, kmax, component.material.k1c 388 | ); 389 | terminate = true; 390 | } 391 | 392 | // The net stress for the component will depend on the shape of a crack. 393 | // We assume it is a corner crack (but the worst case will be an internal crack). 394 | let approx_crack_area = (PI / 4.0) * a.first().unwrap() * a.last().unwrap(); 395 | let component_area = component.sideways * component.forward; 396 | let applied_stress = smax * component_area / (component_area - approx_crack_area); 397 | 398 | if component.material.yield_stress > 0.0 && applied_stress > component.material.yield_stress { 399 | message += &format!( 400 | "{}Note: Assuming a corner crack to check the net-section yield stress criterion.\n", 401 | COMMENT 402 | ); 403 | message += &format!( 404 | "{}approx crack area {}, component area {}\n", 405 | COMMENT, approx_crack_area, component_area 406 | ); 407 | message += &format!( 408 | "{}Failure Event: stress[{}] > yield[{}]\n", 409 | COMMENT, applied_stress, component.material.yield_stress 410 | ); 411 | terminate = true; 412 | } 413 | 414 | FailureResult { 415 | failure: terminate, 416 | messages: message, 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /doc/list.md: -------------------------------------------------------------------------------- 1 | The is the output from `easigrow --list`, slightly reformatted for markdown. 2 | 3 | # easigrow additional information 4 | ## Program Highlights 5 | 6 | * **Sequence filtering** Performs sequence reordering, 7 | turning-point, dead-band, rise-fall filtering and rain-flow 8 | counting. The filtered sequences may be written to a file. 9 | 10 | * **Inbuilt data** Comes with a selection of beta factors, 11 | material data and crack growth models. 12 | 13 | * **Calculated parameters** Calculates additional parameters for 14 | characterising the state of the crack tip so that better 15 | crack growth equations can be developed based on the most 16 | applicable parameters for a material. 17 | 18 | * **Optimisation** Optimises the crack growth model parameters to 19 | minimise the difference between predicted and measured crack 20 | growth. The measured data need not be for the entire history 21 | i.e., one or more fractographic measurements of the width of 22 | a block. 23 | 24 | * **Image generation** Generates a pseudo fractographic image of the fracture 25 | surface to see how easy it is to identify the individual 26 | blocks. 27 | 28 | ## Units 29 | 30 | The internal da/dN data are all in units for stress intensity of 31 | (MPa m^0.5) and growth in (m). Most beta equations use an applied 32 | far field stress which is in units of (MPa). However, the 33 | compact-tension beta factor compact_tada73 uses applied load not stress and 34 | is in units of load of (MN) and coupon dimensions are in (m). The 35 | width and depth will need to be set for the compact-tension beta 36 | function otherwise **easiGrow** will assume an infinite plate and the 37 | crack will not grow. 38 | 39 | ## Output Parameters 40 | 41 | Variable | Description 42 | --- | --- 43 | block |block number 44 | line |line number 45 | a/c |crack aspect ratio 46 | a/d |cracked fraction of forward distance 47 | c/b |cracked fraction of sideways distance 48 | k |current cycle K 49 | dk |current cycle dK 50 | r |current cycle R 51 | beta_a |beta factor at a 52 | beta_c |beta factor at c 53 | a |forward distance of crack from origin 54 | c |sideways distance of crack from origin 55 | da |crack growth increment at a 56 | dc |crack growth increment at c 57 | mono |largest monotonic plastic zone 58 | cyclic |largest cyclic plastic zone 59 | a/mono |ratio of crack length to monotonic plastic zone size 60 | a/cyclic |ratio of crack length to cyclic plastic zone size 61 | mono/da |ratio of current cyclic plastic zone to current da 62 | cyclic/da |ratio of current cyclic plastic zone to current da 63 | peak |scaled peak stress of current cycle 64 | valley |scaled valley stress of current cycle 65 | 66 | ## Beta Models 67 | 68 | Beta | Variables | Description 69 | --------- | -------- | -------------------------------------- 70 | qct-broek86 | |quarter circular crack in an infinite plate in tension [broek86] 71 | seft-newman84 |a/d, a/c, c/b, phi |semi-elliptical surface crack in a finite plate in tension [Newman79] 72 | seit-anderson05 |a/c, phi |semi-elliptical surface crack in an infinite plate in tension [Anderson05] 73 | qcft-murakami87 |a/d |quarter circular corner crack in a finite plate in tension [Murakami87] 74 | qeft-newman84 |a/d, a/c, c/b, phi |quarter elliptical corner crack in a finite plate in tension [Newman79] 75 | eft-newman84 |a/d, a/c, c/b, phi |elliptical crack in a finite plate in tension [Newman79] 76 | sset-tada73 |a/d |single sided edge crack in a plate in tension [Tada73] 77 | dset-tada73 |a/d |double sided edge crack in a plate in tension [Tada73] 78 | compact-tada73 |a/d, depth, width |compact specimen in tension (scale is in load units not stress units) [Tada73] 79 | ct-fedderson66 |a/d |centre cracked plate in tension [Fedderson66] 80 | ct-koiter65 |a/d |centre cracked plate in tension [Koiter65] 81 | qcct-mcdonald07 |a/d |vertically constrained coupon with corner crack in tension [McDonald07] 82 | ssht-bowie56 |a/r |single sided through crack in a circular hole in tension [Bowie56] 83 | dsht-bowie56 |a/r |double sided crack through in a circular hole in tension [Bowie56] 84 | dccht-newman81 |a/d, a/c, c/b, a/r, phi |double sided corner crack in a hole in tension [Newman81] 85 | serbb-shin04 |a/d, a/c |semi-elliptical surface crack in a round bar in bending [shin04] 86 | serbb-murakami87 |a/d, a/c |semi-elliptical surface crack in a round bar in bending [Murakami87] 87 | serbt-murakami87 |a/d, a/c |semi-elliptical surface crack in a round bar in tension [Murakami87] 88 | serbb-murakami86 |a/d, a/c |semi-elliptical surface crack in a round bar in bending [Murakami86] 89 | serbt-murakami86 |a/d, a/c |semi-elliptical surface crack in a round bar in tension [Murakami86] 90 | esb-murakami87 |a/d |edge crack in a strip in bending [Murakami87] 91 | est-murakami87 |a/d |edge crack in a strip in tension [Murakami87] 92 | file:FILE |a/d, a/c |read FILE for beta values 93 | 94 | ## Cycle Counting Models 95 | 96 | Crack growth is calculated for each cycle. The cyles can 97 | be input directly or extracted from a sequence. The way the cycles are 98 | determined affects the growth. The methods for extracting cycles from 99 | a sequence are: 100 | 101 | 1. `rainflow` Crack growth is calculated from rainflow cycles i.e. the 102 | stress intensity range comes from the range of the rainflow 103 | cycles. Note this has a slight re-ordering effect that may upset 104 | the order of any image plots created. 105 | 106 | 2. `tension` Crack growth calculated from tension part of cycle i.e. from a valley 107 | to the next peak. The striation pattern follows these tension cycles. 108 | 109 | ## da/dN data 110 | 111 | The da/dN model consists of EQUATION:material where the 112 | equation variable specifies the name of the da/dN equation and is 113 | one of [nasgro, paris, forman, walker, burchill, hartman, white, file] 114 | The material variable specifies the name of the parameters to use for 115 | that equation. If the values are given in --parameters they will 116 | be used instead of the standard library values. 117 | 118 | *Name* |*Reference* |*Coefficients* 119 | --------------- | -------- | -------------------------------------- 120 | paris:default |[none] |1.00000e-10 3.00000e0 121 | walker:default |[none] |1.00000e-10 5.00000e-1 3.00000e0 122 | forman:default |[none] |1.00000e-10 3.00000e0 6.00000e1 123 | nasgro:default |[nasgro:aa7050t7451-LT, NASGR04.0] |3.00000e-1 2.00000e0 3.51600e1 8.00000e-1 2.20000e0 1.00000e-1 1.00000e0 1.00000e0 6.35000e-10 2.50000e0 3.81000e-5 124 | nasgro:aa7050t7451-LT |[Forman05] |3.00000e-1 2.00000e0 3.51600e1 8.00000e-1 2.20000e0 1.00000e-1 1.00000e0 1.00000e0 6.35000e-10 2.50000e0 3.81000e-5 125 | burchill:default |[none] |1.00000e-10 3.00000e0 1.00000e-10 3.00000e0 126 | kujawski:default |[none] |1.00000e-10 3.00000e0 5.00000e-1 127 | hartman:default |[none] |1.00000e-10 1.00000e0 3.00000e1 3.00000e0 128 | paris:newman-aa7050t7451 |[none] |1.59300e-11 3.66800e0 129 | forman:aa2024t3-sheet |[Schwarmann86] |7.13000e-9 2.70000e0 7.13000e1 130 | forman:aa2024t351-plate |[Schwarmann86] |5.00000e-9 2.88000e0 6.32000e1 131 | forman:aa2024t4-sheet |[Schwarmann86] |8.57000e-9 2.60000e0 5.81000e1 132 | forman:aa2024t6-sheet |[Schwarmann86] |2.00000e-8 2.62000e0 6.98000e1 133 | forman:aa2024t8-sheet |[Schwarmann86] |1.33000e-8 2.65000e0 6.53000e1 134 | forman:aa2024t851-plate |[Schwarmann86] |7.72000e-9 2.78000e0 6.14000e1 135 | forman:aa2219t851-plate |[Schwarmann86] |4.84000e-8 2.16000e0 5.75000e1 136 | forman:aa2618t6-sheet |[Schwarmann86] |8.56000e-9 2.58000e0 4.59000e1 137 | forman:aa6061t6-sheet |[Schwarmann86] |2.27000e-7 6.01000e1 1.66000e0 138 | forman:aa6061t651-plate |[Schwarmann86] |9.60000e-8 1.84000e0 4.12000e1 139 | forman:aa7010t73651-plate |[Schwarmann86] |2.06000e-8 2.46000e0 4.60000e1 140 | forman:aa7050t7352-forging |[Schwarmann86] |2.75000e-9 3.29000e0 6.40000e1 141 | forman:aa7050t73651-plate |[Schwarmann86] |4.11000e-9 2.98000e0 5.50000e1 142 | forman:aa7075t6-sheet |[Schwarmann86] |1.37000e-8 3.02000e0 6.39000e1 143 | forman:aa7075t7351 |[Schwarmann86] |6.27000e-9 2.78000e0 5.58000e1 144 | forman:aa7175t3652-forging |[Schwarmann86] |2.61000e-9 2.91000e0 3.80000e1 145 | forman:aa7178t651-plate |[Schwarmann86] |3.74000e-8 2.06000e0 3.07000e1 146 | forman:aa7475t7351-plate |[Schwarmann86] |3.24000e-8 2.32000e0 7.82000e1 147 | forman:aa7475t76-sheet |[Schwarmann86] |6.54000e-8 2.18000e0 7.99000e1 148 | forman:aa7475t7651-plate |[Schwarmann86] |9.30000e-9 2.73000e0 6.31000e1 149 | forman:a357t6-sandcasting |[Schwarmann86] |2.19000e-9 2.94000e0 4.15000e1 150 | forman:a357t6-investmentcasting |[Schwarmann86] |6.65000e-9 2.40000e0 3.82000e1 151 | hartman:jones13-aa7050t7451 |[jones13] |7.00000e-10 1.00000e-1 4.70000e1 2.00000e0 152 | white:barter14-aa7050t7451 |[white15] |2.54819e-1 1.10247e0 4.35832e0 2.30859e1 3.42017e-2 4.71784e-1 3.15400e1 153 | white:chan16-aa7050t7451 |[] |2.91862e-1 1.26351e0 3.55283e0 2.22432e1 3.92409e-2 5.55131e-1 4.14592e1 154 | file:FILE | |Read FILE of tabular dadn data. 155 | 156 | ## File formats 157 | 158 | ### Crack file 159 | 160 | The crack growth file is in the following format: 161 | 162 | ``` 163 | 164 | ... 165 | ``` 166 | 167 | or 168 | 169 | ``` 170 | 171 | ... 172 | ``` 173 | 174 | or 175 | 176 | ``` 177 | 178 | ... 179 | ``` 180 | 181 | Blank lines in the file indicate non-contiguous measurements. If 182 | or are missing the program will assume the readings are 183 | one block apart with each block measured at line 0. Use the same 184 | format for the entire file. Where represents the corresponding 185 | line no. (starting at 0) of the sequence file, and is the 186 | no. of the block at that crack depth. Strictly speaking, the actual 187 | block numbers are not used by easigrow with only the difference between 188 | the block numbers in contiguous measurements used. Easigrow only 189 | matches the average crack growth rate using: 190 | 191 | rate = (growth between measurements) / (no. of blocks between measurements). 192 | 193 | ### Optimise file 194 | 195 | Each line in the optimise file is a list of easigrow command lines 196 | (without the 'easigrow' command) with each line containing the easigrow 197 | options that will best reproduce the crack growth calculation for the 198 | associated crack growth curve that it is trying to match. Note: Only 199 | the material model specified by the main command line that invokes the 200 | optimisation will be used for all crack predictions, since those will 201 | be the parameters that are optimised. Any other material 202 | specifications will be ignored. 203 | 204 | The format of the optimisation file is: 205 | 206 | ``` 207 | ... --crack 208 | ... --crack 209 | ... 210 | ``` 211 | 212 | ### Beta file 213 | 214 | All lines beginning with a # are treated as a comment and ignored. The 215 | format of the beta file is 216 | 217 | ``` 218 | # Comment describing the contents of the file 219 | a/d beta 220 | ... 221 | ``` 222 | 223 | ### Dadn file 224 | 225 | All lines beginning with a # are treated as a comment and ignored. The 226 | format of the file is: 227 | 228 | ``` 229 | # Comment describing the contents of the file 230 | r1 r2 .... 231 | dadn1 deltaK1_r1 deltaK1_r2 .... 232 | dadn2 deltaK2_r1 deltaK2_r2 .... 233 | ... 234 | ``` 235 | 236 | ## References 237 | 238 | [Newman79] J. C. Newman , Jr. and I. S. Raju 239 | Analyses of surface cracks in finite plates under tension or bending loads 240 | NASA Technical Paper 1578 241 | December 1979 242 | 243 | [Newman81] J. C. Newman Jr. and I. S. Raju 244 | Stress intensity factor equations for cracks 245 | in three-dimensional finite bodies, 246 | NASA Technical Memorandum 83299, 1981 p 1--49. 247 | 248 | [Newman81] J. C. Newman Jr. and I. S. Raju 249 | Stress-intensity factor equations for 250 | cracks in three-dimensional finite bodies 251 | subjected to tension and bending loads, 252 | NASA Technical Memorandum 85739, 1984. 253 | 254 | [Anderson05] T. L. Anderson 255 | Fracture Mechanics - Fundamentals and Applications 256 | Taylor and Francis 3rd Edition 2005 257 | 258 | [Tada73] H. Tada, P. C. Paris and G. R. Irwin 259 | The Stress Analysis of Cracks Handbook 260 | 1973 261 | 262 | [Murakami87] Y. Murakami 263 | Stress Intensity Factors Handbook. Vol 2 264 | Pergamon Press, Oxford, , 1987 265 | 266 | [Murakami87a] Yukitaka Murakami and Hideto Tsuru 267 | Stress Intensity factor equations for a semi-elliptical surface crack in a shaft under bending 268 | 1986 269 | 270 | [Schwarmann86] L. Schwarmann 271 | Material Data of High-Strength Aluminium Alloys for Durability Evaluation of Structures 272 | Aluminium-Verlag 1986 273 | Note: The data from this report has been converted from mm/cycle to m/cyclic by factoring cf by 1e3. 274 | 275 | [Fedderson66] 276 | Taken from Damage Tolerant Design handbook from AFGROW documentation. 277 | 278 | [Kujawski01] Daniel Kujawski, 279 | A fatigue crack driving force parameter with load ratio effects 280 | International Journal of Fatigue, Vol 23, S239-S246, 2001 281 | 282 | [Walker70] K. Walker 283 | The effect of stress ratio during crack propagation and fatigue for {2024-T3} and {7075-T6} aluminum 284 | Effects of Environment and Complex Load History for Fatigue Life, 285 | American Society for Testing and Materials,Special Technical Publication 462, 1970 286 | 287 | [Jones12] Jones, R., Molent, L. & Walker, K. 288 | Fatigue crack growth in a diverse 289 | range of materials, International Journal of Fatigue Vol. 40,pages 43--50, 2012 290 | 291 | [Hartman70] A. Hartman and J. Schijve 292 | The effects of environment and load frequency on the 293 | crack propagation law for macro fatigue crack growth in aluminum alloys, 294 | Engineering Fracture Mechanics, Vol. 1(4), 1970 295 | 296 | [Shin04] C.S. Shin and C. Q. CAI 297 | Experimental and finite element analyses on stress intensity 298 | factors of an elliptical surface crack in a circular shaft under 299 | tension and bending, 300 | International Journal of Fracture 129: 239–264, 2004. 301 | 302 | [Forman05] R. G. Forman, V. Shivakumar, J. W. Cardinal , L. C. Williams and P. C. McKeighan 303 | Fatigue Crack Growth Database for Damage Tolerance Analysis, 304 | DOT/FAA/AR-05/15, 2005. 305 | 306 | --------------------------------------------------------------------------------