Skip to content

Commit

Permalink
Merge pull request #68 from librasteve/version2
Browse files Browse the repository at this point in the history
Version2
  • Loading branch information
librasteve authored Jun 21, 2024
2 parents fd8f84d + 617df18 commit 4cd3e94
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 86 deletions.
4 changes: 2 additions & 2 deletions META6.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "Physics::Measure",
"description": "Some support for physical measurements.",
"version": "1.0.21",
"version": "2.0.0",
"perl": "6.d",
"authors": [
"librasteve"
],
"auth": "zef:librasteve",
"depends": [
"Physics::Unit",
"Physics::Unit:ver<2+>:api<2>",
"Physics::Error"
],
"build-depends": [],
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
[![License: Artistic-2.0](https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg)](https://opensource.org/licenses/Artistic-2.0)
[![raku-physics-measure -> DH](https://github.com/librasteve/raku-Physics-Measure/actions/workflows/measure-weekly.yaml/badge.svg)](https://github.com/librasteve/raku-Physics-Measure/actions/workflows/measure-weekly.yaml)

# Version 2+

This version of Physics::Measure has been adapted to work with the new Physics::Unit:ver<2+>:api<2> release.

# raku-Physics-Measure
Provides Measure objects that have value, units and error and can be used in many common physics calculations. Uses [Physics::Unit](https://github.com/librasteve/raku-Physics-Unit) and [Physics::Error](https://github.com/librasteve/raku-Physics-Error).

Expand Down Expand Up @@ -301,17 +305,18 @@ say $po.pretty; #25 m²⋅s⁻³⋅kg (SI recommended
In a small number of case, the same units are used by different unit Types. Type hints
steer type inference:
```perl6
our %type-hints = %(
Area => <Area FuelConsumption>,
Energy => <Energy Torque>,
Momentum => <Momentum Impulse>,
Frequency => <Frequency Radioactivity>,
has %.type-hint = %(
Area => <Area FuelConsumption>,
Energy => <Energy Torque>,
Momentum => <Momentum Impulse>,
Frequency => <Frequency Radioactivity>,
SpecificEnergy => <SpecificEnergy Dose>,
);
```

To adjust this, you can delete the built in key and replace it with your own:
```perl6
my %th := %Physics::Unit::type-hints;
my %th := Unit.type-hint;

#default type-hints
my $en1 = ♎️'60 J'; #'$en1 ~~ Energy';
Expand All @@ -330,7 +335,7 @@ my $tq2 = $fo3 * $le2; #'$tq2 ~~ Torque';

To make a custom Measure, you can use this incantation:
```perl6
GetMeaUnit('nmile').NewType('Reach');
Measure.unit-find('nmile').type-bind('Reach');

class Reach is Measure {
has $.units where *.name eq <nm nmile nmiles>.any;
Expand Down
1 change: 0 additions & 1 deletion bin/synopsis-1.raku
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use Physics::Measure :ALL;
#see https://github.com/librasteve/raku-Physics-Measure/blob/master/README.md for explanation

my \d = 42m; say ~d; #42m
dd d;
my \t = 10s; say ~t; #10s

my \u = d / t; say ~u; #4.2m/s
Expand Down
103 changes: 59 additions & 44 deletions lib/Physics/Measure.rakumod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
unit module Physics::Measure:ver<1.0.21>:auth<Steve Roe ([email protected])>;
unit module Physics::Measure:ver<2.0.0>:auth<Steve Roe ([email protected])>;
use Physics::Unit;
use Physics::Error;

Expand All @@ -9,7 +9,8 @@ use Physics::Error;
# use Physics::Measure :ALL; ...13s first-, 2.8s pre- compiled

# speed (2024)
# use Physics::Measure :ALL; ...4.4s first-, 0.9s pre- compiled
# use Physics::Measure :ALL; ...4.4s first-, 0.9s pre- compiled


#This module uses Type Variables such as ::T,::($s)
#viz. http://www.jnthn.net/papers/2008-yapc-eu-raku6types.pdf
Expand All @@ -34,20 +35,11 @@ my regex number {
<?{ +"$/" ~~ Real }> #assert coerces via '+' to Real
}

##### Passthrough of Physics::Unit::GetUnit #####

#| design intent is for Measure (.new) to encapsulate Physics::Unit
#| objective is to eliminate 'use lozenges' and to shorten 'use list'
#| occasionally need this for eg. instantiate 'J' to autoreduce 'kg m^2/s^2'

sub GetMeaUnit( $u ) is export {
GetUnit( $u )
}

########## Classes & Methods ##########

class Time { ... }
class Dimensionless { ... }
class Synthetic {...}

class Measure is export {
#Parent class for physical quantities with value, units & error
Expand All @@ -59,7 +51,7 @@ class Measure is export {

#### Constructors ####
multi method new( :$value, :$units, :$error ) { say "new from attrs" if $db;
self.bless( :$value, units => GetUnit($units), error => Error.new(:$error, :$value) )
self.bless( :$value, units => Unit.find($units), error => Error.new(:$error, :$value) )
}
multi method new( ::T: Measure:D $m ) { say "new from Measure" if $db;
my $value = $m.value;
Expand All @@ -69,13 +61,15 @@ class Measure is export {
}
multi method new( Str:D $string ) { say "new from Str" if $db;
my ( $value, $units, $error ) = Measure.defn-extract( $string );
$units = GetUnit( $units );
my $type = $units.type( :just1 ) || 'Measure';
$units = Unit.find( $units );

my $type = $units.type || 'Measure';
$type = 'Synthetic' if $type ~~ /synthetic/;
::($type).new( :$value, :$units, :$error )
}
multi method new( Duration:D $d ) { say "new from Duration" if $db;
my $value = +"$d"; #extract value of Duration in s
Time.new( :$value, units => GetUnit('s') ) #error => Any
Time.new( :$value, units => Unit.find('s') ) #error => Any
}
method clone( ::T: ) { say "cloning " ~ T.^name if $db;
T.new: self
Expand All @@ -85,7 +79,8 @@ class Measure is export {
}

#### Class Methods ####
#baby Grammar for initial extraction of definition from Str (value/unit/error)

#| baby Grammar for initial extraction of definition from Str (value/unit/error)
method defn-extract( Measure:U: Str:D $s ) {
#handle eg. <45°30′30″>
#<°> is U+00B0 <′> is U+2032 <″> is U+2033
Expand Down Expand Up @@ -141,6 +136,11 @@ class Measure is export {
}
}

#| passthrough of Physics::Unit.find top shorten 'use list'
method unit-find(Measure:U: $u ) {
Unit.find( $u )
}

#### Coercion & Output ####
method Real { $.value }

Expand Down Expand Up @@ -253,9 +253,11 @@ class Measure is export {

my $value = $l.value * $r.value;
my ( $type, $units ) = $l.units.multiply( $r.units );

my ( $error, $round ) = $l.add-error-rel( $r, $value );
$error .= round($round) if $round && $round != 0;

$type = 'Synthetic' if $type ~~ /synthetic/;
::($type).new( :$value, :$units, :$error );
}
method multiply-const(Real:D $r) {
Expand All @@ -273,9 +275,11 @@ class Measure is export {

my $value = $l.value / $r.value;
my ( $type, $units ) = $l.units.divide( $r.units );

my ( $error, $round ) = $l.add-error-rel( $r, $value );
$error .= round($round) if $round && $round != 0;

$type = 'Synthetic' if $type ~~ /synthetic/;
::($type).new( :$value, :$units, :$error );
}
method divide-by-const( Real:D $r ) {
Expand All @@ -288,13 +292,18 @@ class Measure is export {
return self
}
method reciprocal { #eg. 1 / Time => Frequency
my $r = self.rebase;
my $r = self.rebase; #ok

my $value = 1 / $r.value;
my ( $type, $units ) = GetUnit('unity').divide( $r.units );

my $numerator = Unit.new: defn => 'unity';

my ( $type, $units ) = $numerator.divide( $r.units );

my $round = $r.error.denorm[1] with $r.error;
my $error = ( $r.error.relative * $value ).round($round) with $r.error;

$type = 'Synthetic' if $type ~~ /synthetic/;
::($type).new( :$value, :$units, :$error );
}
method power( Int:D $n ) { #eg. Area ** 2 => Distance
Expand All @@ -313,6 +322,7 @@ class Measure is export {
my $round = $l.error.denorm[1] / (10 ** $n) with $l.error;
my $error = ( $l.error.relative / $n * $value ).round($round) with $l.error;

$type = 'Synthetic' if $type ~~ /synthetic/;
::($type).new( :$value, :$units, :$error );
}
method sqrt() {
Expand All @@ -324,12 +334,12 @@ class Measure is export {
#| convert units and adjust value
method in( ::O: $to ) {
my $ouo = $.units; #old unit object
my $nuo = GetUnit( $to ); #new unit object
my $nuo = Unit.find( $to ); #new unit object

my $n-type = $nuo.type( :just1 );
my $n-type = $nuo.type;

#allow new type to match old eg. if allomorph
if not ::O ~~ ::($n-type) { #ie. is Distance ~~ Length
#allow new type to match old eg. Distance ~~ Length
if not ::O ~~ ::($n-type) {
die "cannot convert in to different type $n-type"
}

Expand All @@ -339,9 +349,9 @@ class Measure is export {
::($n-type).new( :$value, units => $nuo, :$error )
}

#| adjust prefix (affix) to optimize value significance
#| adjust prefix (postfix) to optimize value significance
method norm {
my %abn = GetAffixByName;
my %abn = Unit.postfix-to-defn;

#try to match via unit defn eg. petahertz
my $defn = self.units.defn;
Expand All @@ -352,8 +362,9 @@ class Measure is export {
my $afx-name = %abn.keys.grep(/^ $name $/ ).first;

#setup some hashes and arrays
my %pfix2fact = GetPrefixToFactor;
my %fact2pfix = %pfix2fact.kv.reverse;
my %pfix2fact = Unit.prefix-to-factor;

my %fact2pfix = %pfix2fact.kv.reverse;
%fact2pfix{'1'} = ''; #plug gap in factors for vanilla base units
my @pfixs = %pfix2fact.keys;

Expand Down Expand Up @@ -386,15 +397,14 @@ class Measure is export {
return $res;
}

#| convert to base (prototype) unit of type
method rebase {
self.in( GetPrototype( self.units.type( :just1 ) ))
}
#`[
method si {
self.rebase
}
#]
#| convert into base unit for type
method rebase {
return self if self.units.type ~~ /synthetic/;

return self if self.units.same-unit: self.units.type-to-unit;

self.in( self.units.type-to-unit );
}

#| compare units
method cmp( $a: $b ) {
Expand Down Expand Up @@ -526,6 +536,10 @@ multi atan( Numeric:D $x, Str :$units! ) is export {

######## Child Classes ########

# FIXME - defile for localization override?

class Synthetic is Measure is export {}

#SI Base Units
class Length is Measure is export {}
class Mass is Measure is export {}
Expand Down Expand Up @@ -571,6 +585,7 @@ class FuelConsumption is Measure is export {}
class FuelEfficiency is Measure is export {}
class Flow is Measure is export {}
class SpecificEnergy is Measure is export {}
class SpecificPower is Measure is export {}
class Irradiance is Measure is export {}
class Insolation is Measure is export {}
class ThermalResistance is Measure is export {}
Expand Down Expand Up @@ -706,26 +721,26 @@ multi infix:<!=> ( Measure:D $a, Measure:D $b ) is equiv( &infix:<!=> ) is expor
else { return True; }
}

##### Affix Operators #####
##### Postfix Operators #####

#`[[
Affix Operators combine the notions of:
Postfix Operators combine the notions of:
1. SI Prefixes e.g. c(centi-), k(kilo-) that make compound units such as cm, km, kg
2. Raku Postfixes e.g. $l = 42cm; operators which work on the preceding value
We use the term Affix to indicate that both concepts are provided by this code:
We use the term Postfix to indicate that both concepts are provided by this code:
1. Construction of the cross product of SI Prefixes (20) with ( SI Base (7) + Derived (20) ) Units
2. Declaration of the resulting ~540 Unit instances and matching Raku Postfix operators
Now you can simply go 'my $l = 1km;' to construct a new Measure object with value => 1 and units => 'km'
#]]

my %affix-by-name = GetAffixByName;
my %affix-syns-by-name = GetAffixSynsByName;
#my %postfix-to-defn = Unit.postfix-to-defn;
#my %postfix-syns-by-name = Unit.postfix-to-syns;

sub do-postfix( Real $v, Str $cn ) is export {
my $u = Unit.new( defn => $cn, names => %affix-syns-by-name{$cn} );
my $t = $u.type(:just1);
my $u = Unit.new( defn => $cn, names => Unit.postfix-to-syns{$cn} );
my $t = $u.type;
return ::($t).new(value => $v, units => $u);
}

Expand All @@ -740,7 +755,7 @@ sub postfix:<steradian> (Real:D $x) is export { do-postfix($x,'steradian') }
#| then put in all the regular combinations programmatically
#| viz. https://docs.raku.org/language/modules#Exporting_and_selective_importing
my package EXPORT::ALL {
for %affix-by-name.keys -> $u {
for Unit.postfix-to-defn.keys -> $u {
OUR::{'&postfix:<' ~ $u ~ '>'} := sub (Real:D $x) { do-postfix($x,"$u") };
}
}
Expand Down
36 changes: 36 additions & 0 deletions raku-Physics-Measure.iml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,41 @@
<sourceFolder url="file://$MODULE_DIR$/t" isTestSource="true" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="Physics::Error" type="perl6">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="raku://1166070976:Physics::Error!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="YAMLish" type="perl6">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="raku://1166070978:YAMLish!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="MIME::Base64" type="perl6">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="raku://1166070978:MIME::Base64!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="Physics::Unit:ver&lt;2+&gt;:api&lt;2&gt;" type="perl6">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="raku://1166070978:Physics::Unit:ver&lt;2+&gt;:api&lt;2&gt;!/" />
</SOURCES>
</library>
</orderEntry>
</component>
</module>
2 changes: 1 addition & 1 deletion t/01-new.rakutest
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use Physics::Measure;

#Part 1: Default new Constructor

my $u = GetMeaUnit( 'm' );
my $u = Measure.unit-find( 'm' );
is $u.name,'m', '$u.name exists';
is ~$u.type,'Length', '$u.units made';

Expand Down
2 changes: 1 addition & 1 deletion t/02-asn.rakutest
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plan 11;

use Physics::Measure;

my $u = GetMeaUnit('m'); #get Unit object to construct a Measure
my $u = Measure.unit-find('m'); #get Unit object to construct a Measure
my Length $a .=new(value => 1e4, units => $u);

is $a.WHAT, Length, 'ass:2xdef';
Expand Down
Loading

0 comments on commit 4cd3e94

Please sign in to comment.