toji 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +39 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/example/koji_recipe.rb +34 -0
  11. data/example/make_koji.rb +18 -0
  12. data/example/rice_recipe.rb +28 -0
  13. data/example/three_step_mashing_recipe.rb +65 -0
  14. data/lib/toji.rb +12 -0
  15. data/lib/toji/graph.rb +6 -0
  16. data/lib/toji/graph/ab.rb +75 -0
  17. data/lib/toji/ingredient.rb +8 -0
  18. data/lib/toji/ingredient/koji.rb +19 -0
  19. data/lib/toji/ingredient/koji/actual.rb +21 -0
  20. data/lib/toji/ingredient/koji/actual_fermentable.rb +15 -0
  21. data/lib/toji/ingredient/koji/base.rb +26 -0
  22. data/lib/toji/ingredient/koji/expected.rb +51 -0
  23. data/lib/toji/ingredient/koji/expected_fermentable.rb +15 -0
  24. data/lib/toji/ingredient/rice.rb +19 -0
  25. data/lib/toji/ingredient/rice/actual.rb +18 -0
  26. data/lib/toji/ingredient/rice/actual_steamable.rb +27 -0
  27. data/lib/toji/ingredient/rice/base.rb +40 -0
  28. data/lib/toji/ingredient/rice/expected.rb +46 -0
  29. data/lib/toji/ingredient/rice/expected_steamable.rb +29 -0
  30. data/lib/toji/ingredient/yeast.rb +9 -0
  31. data/lib/toji/ingredient/yeast/base.rb +21 -0
  32. data/lib/toji/ingredient/yeast/red_star.rb +30 -0
  33. data/lib/toji/progress.rb +11 -0
  34. data/lib/toji/progress/job.rb +165 -0
  35. data/lib/toji/progress/job_accessor.rb +13 -0
  36. data/lib/toji/progress/jobs.rb +142 -0
  37. data/lib/toji/progress/make_koji.rb +100 -0
  38. data/lib/toji/progress/moromi.rb +276 -0
  39. data/lib/toji/progress/shubo.rb +158 -0
  40. data/lib/toji/recipe.rb +9 -0
  41. data/lib/toji/recipe/koji_rate.rb +16 -0
  42. data/lib/toji/recipe/rice_rate.rb +10 -0
  43. data/lib/toji/recipe/rice_rate/base.rb +21 -0
  44. data/lib/toji/recipe/rice_rate/cooked.rb +61 -0
  45. data/lib/toji/recipe/rice_rate/steamed.rb +42 -0
  46. data/lib/toji/recipe/step.rb +65 -0
  47. data/lib/toji/recipe/three_step_mashing.rb +87 -0
  48. data/lib/toji/version.rb +3 -0
  49. data/toji.gemspec +36 -0
  50. metadata +178 -0
@@ -0,0 +1,158 @@
1
+ module Toji
2
+ module Progress
3
+ class Shubo
4
+ extend JobAccessor
5
+
6
+ TEMPLATES = {
7
+ default: [
8
+ Job.new(
9
+ elapsed_time: 0 * Job::DAY,
10
+ id: :mizukoji,
11
+ after_temp: 12.0,
12
+ acid: 13.0,
13
+ ),
14
+ Job.new(
15
+ elapsed_time: 0 * Job::DAY + 1 * Job::HOUR,
16
+ id: :shikomi,
17
+ after_temp: 20.0,
18
+ acid: 13.0,
19
+ ),
20
+ Job.new(
21
+ elapsed_time: 1 * Job::DAY,
22
+ id: :utase,
23
+ after_temp: 14.0,
24
+ baume: 15.0,
25
+ acid: 13.0,
26
+ ),
27
+ Job.new(
28
+ elapsed_time: 2 * Job::DAY,
29
+ before_temp: 8.0,
30
+ after_temp: 11.0,
31
+ baume: 16.0,
32
+ acid: 13.0,
33
+ warming: Job::WARM_DAKI,
34
+ ),
35
+ Job.new(
36
+ elapsed_time: 3 * Job::DAY,
37
+ before_temp: 10.0,
38
+ after_temp: 13.0,
39
+ baume: 16.5,
40
+ acid: 13.5,
41
+ warming: Job::WARM_DAKI,
42
+ ),
43
+ Job.new(
44
+ elapsed_time: 4 * Job::DAY,
45
+ before_temp: 12.0,
46
+ after_temp: 15.0,
47
+ baume: 17.0,
48
+ acid: 13.5,
49
+ warming: Job::WARM_DAKI,
50
+ ),
51
+ Job.new(
52
+ elapsed_time: 5 * Job::DAY,
53
+ after_temp: 14.0,
54
+ baume: 17.0,
55
+ acid: 14.0,
56
+ warming: Job::WARM_ANKA,
57
+ ),
58
+ Job.new(
59
+ elapsed_time: 6 * Job::DAY,
60
+ after_temp: 20.0,
61
+ baume: 17.0,
62
+ acid: 14.5,
63
+ warming: Job::WARM_ANKA,
64
+ ),
65
+ Job.new(
66
+ elapsed_time: 7 * Job::DAY,
67
+ after_temp: 20.0,
68
+ baume: 14.0,
69
+ acid: 15.5,
70
+ ),
71
+ Job.new(
72
+ elapsed_time: 8 * Job::DAY,
73
+ after_temp: 20.0,
74
+ baume: 12.0,
75
+ acid: 16.0,
76
+ ),
77
+ Job.new(
78
+ elapsed_time: 9 * Job::DAY,
79
+ id: :wake,
80
+ after_temp: 20.0,
81
+ baume: 9.0,
82
+ acid: 16.0,
83
+ ),
84
+ Job.new(
85
+ elapsed_time: 10 * Job::DAY,
86
+ after_temp: 15.0,
87
+ baume: 8.0,
88
+ acid: 16.5,
89
+ ),
90
+ Job.new(
91
+ elapsed_time: 11 * Job::DAY,
92
+ after_temp: 12.0,
93
+ baume: 7.0,
94
+ acid: 17.0,
95
+ ),
96
+ Job.new(
97
+ elapsed_time: 12 * Job::DAY,
98
+ after_temp: 10.0,
99
+ baume: 6.0,
100
+ acid: 17.5,
101
+ ),
102
+ Job.new(
103
+ elapsed_time: 13 * Job::DAY,
104
+ after_temp: 9.0,
105
+ baume: 5.0,
106
+ acid: 17.5,
107
+ ),
108
+ ],
109
+ }
110
+
111
+
112
+ job_reader :mizukoji
113
+ job_reader :shikomi
114
+ job_reader :utase
115
+ job_reader :wake
116
+
117
+ def initialize(jobs=[])
118
+ @jobs = Jobs.new(jobs)
119
+ end
120
+
121
+ def add(job)
122
+ @jobs << job
123
+ self
124
+ end
125
+
126
+ def jobs
127
+ @jobs.to_a
128
+ end
129
+
130
+ def days
131
+ (jobs.last.elapsed_time.to_f / Job::DAY).ceil + 1
132
+ end
133
+
134
+ def plot_data
135
+ @jobs.plot_data
136
+ end
137
+
138
+ def plot
139
+ _days = days
140
+
141
+ Plotly::Plot.new(
142
+ data: plot_data,
143
+ layout: {
144
+ xaxis: {
145
+ dtick: 24*60*60,
146
+ tickvals: _days.times.map{|d| d*Job::DAY},
147
+ ticktext: (1.._days).to_a
148
+ }
149
+ }
150
+ )
151
+ end
152
+
153
+ def self.template(key=:default)
154
+ new(TEMPLATES[key])
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,9 @@
1
+ require 'toji/recipe/rice_rate'
2
+ require 'toji/recipe/koji_rate'
3
+ require 'toji/recipe/step'
4
+ require 'toji/recipe/three_step_mashing'
5
+
6
+ module Toji
7
+ module Recipe
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Toji
2
+ module Recipe
3
+ class KojiRate
4
+ attr_reader :tanekoji_rate
5
+ attr_reader :dekoji_rate
6
+
7
+ def initialize(tanekoji_rate, dekoji_rate)
8
+ @tanekoji_rate = tanekoji_rate
9
+ @dekoji_rate = dekoji_rate
10
+ end
11
+
12
+ DEFAULT = new(0.001, 0.18)
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,10 @@
1
+ require 'toji/recipe/rice_rate/base'
2
+ require 'toji/recipe/rice_rate/cooked'
3
+ require 'toji/recipe/rice_rate/steamed'
4
+
5
+ module Toji
6
+ module Recipe
7
+ module RiceRate
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Toji
2
+ module Recipe
3
+ module RiceRate
4
+ module Base
5
+ # 浸漬米吸水率
6
+ attr_reader :soaked_rate
7
+
8
+ # 蒸し前浸漬米吸水率
9
+ # 炊飯の場合は水を追加
10
+ # 蒸しの場合は一晩経って蒸発した分を削減
11
+ attr_reader :before_steaming_rate
12
+
13
+ # 蒸米吸水率
14
+ attr_reader :steamed_rate
15
+
16
+ # 放冷後蒸米吸水率
17
+ attr_reader :cooled_rate
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,61 @@
1
+ module Toji
2
+ module Recipe
3
+ module RiceRate
4
+ class Cooked
5
+ include Base
6
+
7
+ # 炊飯器を使う場合
8
+ # 白米の60%の水で炊くと蒸米吸水率はだいたい38%くらいになる
9
+ # 炊飯、放冷の過程で水分のうち36%程度は蒸発する
10
+
11
+ # 蒸す前
12
+ # ・白米(無洗米コシヒカリ) 85g
13
+ # ・水 50g
14
+ # ・浸漬米吸水率 58.82%
15
+ #
16
+ # 蒸した後
17
+ # ・蒸米 117.5g
18
+ # ・蒸米吸水率 38.24%
19
+
20
+ def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
21
+ @soaked_rate = soaked_rate
22
+ @before_steaming_rate = before_steaming_rate
23
+ @steamed_rate = steamed_rate
24
+ @cooled_rate = cooled_rate
25
+ end
26
+
27
+
28
+ DEFAULT = new(0.35, 0.5, 0.40, 0.36)
29
+
30
+ @@rates = [
31
+ #new(0.60, 0.380+0.1, 0.380),
32
+ #new(0.55, 0.360+0.1, 0.360),
33
+ #new(0.50, 0.335+0.1, 0.335),
34
+ new(0.35, 0.5, 0.40, 0.36),
35
+ new(0.33, 0.45, 0.40, 0.35),
36
+ ]
37
+
38
+ def self.soaked_rate(val)
39
+ @@rates.select{|r| r.soaked_rate==val}.first
40
+ end
41
+
42
+ def self.before_steaming_rate(val)
43
+ @@rates.select{|r| r.before_steaming_rate==val}.first
44
+ end
45
+
46
+ def self.steamed_rate(val)
47
+ @@rates.select{|r| r.steamed_rate==val}.first
48
+ end
49
+
50
+ def self.cooled_rate(val)
51
+ @@rates.select{|r| r.cooled_rate==val}.first
52
+ end
53
+ end
54
+
55
+
56
+ def self.cooked(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
57
+ Cooked.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+ module Toji
2
+ module Recipe
3
+ module RiceRate
4
+ class Steamed
5
+ include Base
6
+
7
+ def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
8
+ @soaked_rate = soaked_rate
9
+ @before_steaming_rate = before_steaming_rate
10
+ @steamed_rate = steamed_rate
11
+ @cooled_rate = cooled_rate
12
+ end
13
+
14
+ def before_steaming_rate
15
+ @before_steaming_rate || begin
16
+ @soaked_rate - 0.04
17
+ end
18
+ end
19
+
20
+
21
+ DEFAULT = new(0.33, nil, 0.41, 0.33)
22
+
23
+ def self.soaked_rate(val)
24
+ @@rates.select{|r| r.soaked_rate==val}.first
25
+ end
26
+
27
+ def self.steamed_rate(val)
28
+ @@rates.select{|r| r.steamed_rate==val}.first
29
+ end
30
+
31
+ def self.cooled_rate(val)
32
+ @@rates.select{|r| r.cooled_rate==val}.first
33
+ end
34
+ end
35
+
36
+
37
+ def self.steamed(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
38
+ Steamed.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,65 @@
1
+ module Toji
2
+ module Recipe
3
+ class Step
4
+ attr_reader :rice
5
+ attr_reader :koji
6
+ attr_reader :water
7
+
8
+ def initialize(rice, koji, water)
9
+ @rice = Ingredient::Rice::Expected.create(rice)
10
+ @koji = Ingredient::Koji::Expected.create(koji)
11
+ @water = water.to_f
12
+ end
13
+
14
+ # 総米
15
+ def rice_total
16
+ @rice.raw + @koji.raw
17
+ end
18
+
19
+ # 総重量
20
+ def weight_total
21
+ @rice.cooled + @koji.dekoji + @water
22
+ end
23
+
24
+ # 麹歩合
25
+ # 原料白米に占める麹米の割合
26
+ # 留め仕込みまでの総米重量の20〜22%が標準である
27
+ # なお、留め仕込みまでの麹歩合が20%を下回ると蒸米の溶解糖化に影響が出るので注意がいる
28
+ # 出典: 酒造教本 P95
29
+ def koji_rate
30
+ @koji.raw / rice_total
31
+ end
32
+
33
+ # 汲水歩合
34
+ #
35
+ # 酒母: 110%が標準、高温糖化酒母は150〜180%
36
+ # 添: 85〜100%の範囲で90%が標準、湧き進め型は歩合が高い
37
+ # 仲: 120%が標準
38
+ # 留: 130〜150%の範囲
39
+ # 全体: 留までの総米に対し120〜130%、標準は125%、高級酒は130〜145%である
40
+ #
41
+ # 出典: 酒造教本 P96
42
+ def water_rate
43
+ @water / rice_total
44
+ end
45
+
46
+ def +(other)
47
+ if Step===other
48
+ self.class.new(@rice + other.rice, @koji + other.koji, @water + other.water)
49
+ else
50
+ x, y = other.coerce(self)
51
+ x + y
52
+ end
53
+ end
54
+
55
+ def *(other)
56
+ if Integer===other || Float===other
57
+ self.class.new(@rice * other, @koji * other, @water * other)
58
+ else
59
+ x, y = other.coerce(self)
60
+ x * y
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,87 @@
1
+ module Toji
2
+ module Recipe
3
+ class ThreeStepMashing
4
+
5
+ STEP_NAMES = ["酒母", "初添", "仲添", "留添"]
6
+
7
+ @@template = [
8
+ Step.new(93, 47, 170),
9
+ Step.new(217, 99, 270),
10
+ Step.new(423, 143, 670),
11
+ Step.new(813, 165, 1330),
12
+ ]
13
+ #@@template = [
14
+ # Step.new(45, 20, 70),
15
+ # Step.new(100, 40, 130),
16
+ # Step.new(215, 60, 330),
17
+ # Step.new(360, 80, 630),
18
+ #]
19
+
20
+ attr_reader :yeast
21
+ attr_reader :steps
22
+
23
+ def initialize(yeast_cls, total, template=@@template)
24
+ @total = total
25
+ rate = total / template.map(&:weight_total).sum
26
+
27
+ @yeast = yeast_cls.new(total)
28
+
29
+ @steps = template.map {|step|
30
+ step * rate
31
+ }
32
+ end
33
+
34
+ # 内容量の累計
35
+ def cumulative_weight_totals
36
+ total = @steps.map(&:weight_total)
37
+
38
+ total.map.with_index {|x,i|
39
+ total[0..i].inject(:+)
40
+ }
41
+ end
42
+
43
+ # 総米の累計
44
+ def cumulative_rice_totals
45
+ rice_total = @steps.map(&:rice_total)
46
+
47
+ rice_total.map.with_index {|x,i|
48
+ rice_total[0..i].inject(&:+)
49
+ }
50
+ end
51
+
52
+ # 酒母歩合の累計
53
+ def cumulative_shubo_rates
54
+ rice_total = @steps.map(&:rice_total)
55
+ shubo = rice_total[0]
56
+
57
+ rice_total.map.with_index {|x,i|
58
+ shubo / rice_total[0..i].inject(&:+)
59
+ }
60
+ end
61
+
62
+ # 酒母歩合
63
+ #
64
+ # 7%が標準である
65
+ # 汲水歩合が大きい高温糖化酒母では6%程度である
66
+ #
67
+ # 出典: 酒造教本 P96
68
+ def shubo_rate
69
+ cumulative_shubo_rates.last
70
+ end
71
+
72
+ # 白米比率
73
+ #
74
+ # 発酵型と白米比率
75
+ # 発酵の型 酒母 添 仲 留 特徴
76
+ # 湧き進め型 1 2 4 6 短期醪、辛口、軽快な酒質
77
+ # 湧き抑え型 1 2 4 7 長期醪、甘口、おだやかな酒質
78
+ #
79
+ # 出典: 酒造教本 P95
80
+ def rice_rates
81
+ @steps.map {|step|
82
+ step.rice_total / @steps[0].rice_total
83
+ }
84
+ end
85
+ end
86
+ end
87
+ end